diff --git a/git_common.py b/git_common.py index 8e8135744a..e022ff373c 100644 --- a/git_common.py +++ b/git_common.py @@ -6,12 +6,13 @@ # Derived from https://gist.github.com/aljungberg/626518 from __future__ import print_function +from __future__ import unicode_literals import multiprocessing.pool from multiprocessing.pool import IMapIterator def wrapper(func): def wrap(self, timeout=None): - return func(self, timeout=timeout or 1e100) + return func(self, timeout=timeout or 1 << 31) return wrap IMapIterator.next = wrapper(IMapIterator.next) IMapIterator.__next__ = IMapIterator.next @@ -303,11 +304,17 @@ class ProgressPrinter(object): def once(function): """@Decorates |function| so that it only performs its action once, no matter how many times the decorated |function| is called.""" - def _inner_gen(): - yield function() - while True: - yield - return _inner_gen().next + has_run = [False] + def _wrapper(*args, **kwargs): + if not has_run[0]: + has_run[0] = True + function(*args, **kwargs) + return _wrapper + + +def unicode_repr(s): + result = repr(s) + return result[1:] if result.startswith('u') else result ## Git functions @@ -597,8 +604,8 @@ def mktree(treedict): treedict - { name: (mode, type, ref) } """ with tempfile.TemporaryFile() as f: - for name, (mode, typ, ref) in treedict.iteritems(): - f.write('%s %s %s\t%s\0' % (mode, typ, ref, name)) + for name, (mode, typ, ref) in treedict.items(): + f.write(('%s %s %s\t%s\0' % (mode, typ, ref, name)).encode()) f.seek(0) return run('mktree', '-z', stdin=f) @@ -612,7 +619,7 @@ def parse_commitrefs(*commitrefs): * 'cool_branch~2' """ try: - return map(binascii.unhexlify, hash_multi(*commitrefs)) + return [binascii.unhexlify(h) for h in hash_multi(*commitrefs)] except subprocess2.CalledProcessError: raise BadCommitRefException(commitrefs) @@ -751,6 +758,7 @@ def run_with_stderr(*cmd, **kwargs): kwargs.setdefault('shell', False) autostrip = kwargs.pop('autostrip', True) indata = kwargs.pop('indata', None) + decode = kwargs.pop('decode', True) cmd = (GIT_EXE, '-c', 'color.ui=never') + cmd proc = subprocess2.Popen(cmd, **kwargs) @@ -760,8 +768,12 @@ def run_with_stderr(*cmd, **kwargs): raise subprocess2.CalledProcessError(retcode, cmd, os.getcwd(), ret, err) if autostrip: - ret = (ret or '').strip() - err = (err or '').strip() + ret = (ret or b'').strip() + err = (err or b'').strip() + + if decode: + ret = ret.decode('utf-8', 'replace') + err = err.decode('utf-8', 'replace') return ret, err @@ -810,9 +822,9 @@ def status(): def tokenizer(stream): acc = BytesIO() c = None - while c != '': + while c != b'': c = stream.read(1) - if c in (None, '', '\0'): + if c in (None, b'', b'\0'): if len(acc.getvalue()): yield acc.getvalue() acc = BytesIO() @@ -821,12 +833,14 @@ def status(): def parser(tokens): while True: - # Raises StopIteration if it runs out of tokens. - status_dest = next(tokens) + try: + status_dest = next(tokens).decode('utf-8') + except StopIteration: + return stat, dest = status_dest[:2], status_dest[3:] lstat, rstat = stat if lstat == 'R': - src = next(tokens) + src = next(tokens).decode('utf-8') else: src = dest yield (dest, stat_entry(lstat, rstat, src)) @@ -848,7 +862,7 @@ def squash_current_branch(header=None, merge_base=None): # nothing to commit at this point. print('Nothing to commit; squashed branch is empty') return False - run('commit', '--no-verify', '-a', '-F', '-', indata=log_msg) + run('commit', '--no-verify', '-a', '-F', '-', indata=log_msg.encode()) return True @@ -858,7 +872,8 @@ def tags(*args): def thaw(): took_action = False - for sha in (s.strip() for s in run_stream('rev-list', 'HEAD').xreadlines()): + for sha in run_stream('rev-list', 'HEAD').readlines(): + sha = sha.strip().decode('utf-8') msg = run('show', '--format=%f%b', '-s', 'HEAD') match = FREEZE_MATCHER.match(msg) if not match: @@ -899,7 +914,7 @@ def topo_iter(branch_tree, top_down=True): # TODO(iannucci): There is probably a more efficient way to do these. if top_down: while branch_tree: - this_pass = [(b, p) for b, p in branch_tree.iteritems() + this_pass = [(b, p) for b, p in branch_tree.items() if p not in branch_tree] assert this_pass, "Branch tree has cycles: %r" % branch_tree for branch, parent in sorted(this_pass): @@ -907,11 +922,11 @@ def topo_iter(branch_tree, top_down=True): del branch_tree[branch] else: parent_to_branches = collections.defaultdict(set) - for branch, parent in branch_tree.iteritems(): + for branch, parent in branch_tree.items(): parent_to_branches[parent].add(branch) while branch_tree: - this_pass = [(b, p) for b, p in branch_tree.iteritems() + this_pass = [(b, p) for b, p in branch_tree.items() if not parent_to_branches[b]] assert this_pass, "Branch tree has cycles: %r" % branch_tree for branch, parent in sorted(this_pass): @@ -1014,7 +1029,9 @@ def get_branches_info(include_tracking_status): if info.upstream not in info_map and info.upstream not in missing_upstreams: missing_upstreams[info.upstream] = None - return dict(info_map.items() + missing_upstreams.items()) + result = info_map.copy() + result.update(missing_upstreams) + return result def make_workdir_common(repository, new_workdir, files_to_symlink, diff --git a/git_hyper_blame.py b/git_hyper_blame.py index 3e085134db..8b0ee301ee 100755 --- a/git_hyper_blame.py +++ b/git_hyper_blame.py @@ -7,6 +7,7 @@ """ from __future__ import print_function +from __future__ import unicode_literals import argparse import collections @@ -94,11 +95,6 @@ def parse_blame(blameoutput): yield BlameLine(commit, context, lineno_then, lineno_now, False) -def num_codepoints(s): - """Gets the length of a UTF-8 byte string, in Unicode codepoints.""" - return len(s.decode('utf-8', errors='replace')) - - def print_table(table, colsep=' ', rowsep='\n', align=None, out=sys.stdout): """Print a 2D rectangular array, aligning columns with spaces. @@ -112,10 +108,9 @@ def print_table(table, colsep=' ', rowsep='\n', align=None, out=sys.stdout): colwidths = None for row in table: if colwidths is None: - colwidths = [num_codepoints(x) for x in row] + colwidths = [len(x) for x in row] else: - colwidths = [max(colwidths[i], num_codepoints(x)) - for i, x in enumerate(row)] + colwidths = [max(colwidths[i], len(x)) for i, x in enumerate(row)] if align is None: # pragma: no cover align = 'l' * len(colwidths) @@ -123,7 +118,7 @@ def print_table(table, colsep=' ', rowsep='\n', align=None, out=sys.stdout): for row in table: cells = [] for i, cell in enumerate(row): - padding = ' ' * (colwidths[i] - num_codepoints(cell)) + padding = ' ' * (colwidths[i] - len(cell)) if align[i] == 'r': cell = padding + cell elif i < len(row) - 1: @@ -279,7 +274,7 @@ def hyper_blame(ignored, filename, revision='HEAD', out=sys.stdout, try: parsed = cache_blame_from(filename, git_common.hash_one(revision)) except subprocess2.CalledProcessError as e: - err.write(e.stderr) + err.write(e.stderr.decode()) return e.returncode new_parsed = [] @@ -361,7 +356,7 @@ def main(args, stdout=sys.stdout, stderr=sys.stderr): try: repo_root = git_common.repo_root() except subprocess2.CalledProcessError as e: - stderr.write(e.stderr) + stderr.write(e.stderr.decode()) return e.returncode # Make filename relative to the repository root, and cd to the root dir (so diff --git a/git_number.py b/git_number.py index f3d2da7f90..0e3ac16dd1 100755 --- a/git_number.py +++ b/git_number.py @@ -75,7 +75,8 @@ def get_number_tree(prefix_bytes): ref = '%s:%s' % (REF, pathlify(prefix_bytes)) try: - raw = buffer(git.run('cat-file', 'blob', ref, autostrip=False)) + raw = buffer( + git.run('cat-file', 'blob', ref, autostrip=False, decode=False)) return dict(struct.unpack_from(CHUNK_FMT, raw, i * CHUNK_SIZE) for i in xrange(len(raw) / CHUNK_SIZE)) except subprocess2.CalledProcessError: diff --git a/git_rebase_update.py b/git_rebase_update.py index fa5b5c4f80..e1d6b22224 100755 --- a/git_rebase_update.py +++ b/git_rebase_update.py @@ -320,8 +320,8 @@ def main(args=None): else: root_branch = git.root() if return_branch != 'HEAD': - print("%r was merged with its parent, checking out %r instead." % - (return_branch, root_branch)) + print("%s was merged with its parent, checking out %s instead." % + (git.unicode_repr(return_branch), git.unicode_repr(root_branch))) git.run('checkout', root_branch) # return_workdir may also not be there any more. diff --git a/testing_support/git_test_utils.py b/testing_support/git_test_utils.py index e580d7f061..80dd3a26f7 100644 --- a/testing_support/git_test_utils.py +++ b/testing_support/git_test_utils.py @@ -15,13 +15,18 @@ import tempfile import unittest +if sys.version_info.major == 3: + # pylint: disable=redefined-builtin + basestring = (str,) + + def git_hash_data(data, typ='blob'): """Calculate the git-style SHA1 for some data. Only supports 'blob' type data at the moment. """ assert typ == 'blob', 'Only support blobs for now' - return hashlib.sha1('blob %s\0%s' % (len(data), data)).hexdigest() + return hashlib.sha1(b'blob %d\0%s' % (len(data), data)).hexdigest() class OrderedSet(collections.MutableSet): @@ -120,8 +125,8 @@ class GitRepoSchema(object): This is the repo - A - B - C - D - \ E / + A - B - C - D + \\ E / Whitespace doesn't matter. Each line is a declaration of which commits come before which other commits. @@ -166,15 +171,15 @@ class GitRepoSchema(object): is_root = True par_map = copy.deepcopy(self.par_map) while par_map: - empty_keys = set(k for k, v in par_map.iteritems() if not v) + empty_keys = set(k for k, v in par_map.items() if not v) assert empty_keys, 'Cycle detected! %s' % par_map for k in sorted(empty_keys): yield self.COMMIT(k, self.par_map[k], - not any(k in v for v in self.par_map.itervalues()), + not any(k in v for v in self.par_map.values()), is_root) del par_map[k] - for v in par_map.itervalues(): + for v in par_map.values(): v.difference_update(empty_keys) is_root = False @@ -321,7 +326,7 @@ class GitRepo(object): env = self.get_git_commit_env(commit_data) - for fname, file_data in commit_data.iteritems(): + for fname, file_data in commit_data.items(): # If it isn't a string, it's one of the special keys. if not isinstance(fname, basestring): continue @@ -367,7 +372,9 @@ class GitRepo(object): self._date += datetime.timedelta(days=1) else: val = getattr(self, 'DEFAULT_%s' % singleton) - env['GIT_%s' % singleton] = str(val) + if not isinstance(val, str) and not isinstance(val, bytes): + val = str(val) + env['GIT_%s' % singleton] = val return env def git(self, *args, **kwargs): @@ -377,6 +384,7 @@ class GitRepo(object): with open(os.devnull, 'wb') as devnull: output = subprocess.check_output( ('git',) + args, cwd=self.repo_path, stderr=devnull, **kwargs) + output = output.decode('utf-8') return self.COMMAND_OUTPUT(0, output) except subprocess.CalledProcessError as e: return self.COMMAND_OUTPUT(e.returncode, e.output) @@ -418,17 +426,17 @@ class GitRepo(object): stdout = sys.stdout stderr = sys.stderr try: - # "multiple statements on a line" pylint: disable=multiple-statements - with tempfile.TemporaryFile() as out, tempfile.TemporaryFile() as err: - sys.stdout = out - sys.stderr = err - try: - self.run(fn, *args, **kwargs) - except SystemExit: - pass - out.seek(0) - err.seek(0) - return out.read(), err.read() + with tempfile.TemporaryFile('w+') as out: + with tempfile.TemporaryFile('w+') as err: + sys.stdout = out + sys.stderr = err + try: + self.run(fn, *args, **kwargs) + except SystemExit: + pass + out.seek(0) + err.seek(0) + return out.read(), err.read() finally: sys.stdout = stdout sys.stderr = stderr diff --git a/tests/git_common_test.py b/tests/git_common_test.py index 2b12a876af..30ce8cddbc 100755 --- a/tests/git_common_test.py +++ b/tests/git_common_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env vpython3 # Copyright 2013 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -85,7 +85,7 @@ class Support(GitCommonTestBase): add_to_list() add_to_list() - self.assertEquals(testlist, ['dog']) + self.assertEqual(testlist, ['dog']) def slow_square(i): @@ -102,7 +102,7 @@ class ScopedPoolTest(GitCommonTestBase): def testThreads(self): result = [] with self.gc.ScopedPool(kind='threads') as pool: - result = list(pool.imap(slow_square, xrange(10))) + result = list(pool.imap(slow_square, range(10))) self.assertEqual([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], result) def testThreadsCtrlC(self): @@ -110,7 +110,7 @@ class ScopedPoolTest(GitCommonTestBase): with self.assertRaises(KeyboardInterrupt): with self.gc.ScopedPool(kind='threads') as pool: # Make sure this pool is interrupted in mid-swing - for i in pool.imap(slow_square, xrange(20)): + for i in pool.imap(slow_square, range(20)): if i > 32: os.kill(os.getpid(), self.CTRL_C) result.append(i) @@ -119,7 +119,7 @@ class ScopedPoolTest(GitCommonTestBase): def testProcs(self): result = [] with self.gc.ScopedPool() as pool: - result = list(pool.imap(slow_square, xrange(10))) + result = list(pool.imap(slow_square, range(10))) self.assertEqual([0, 1, 4, 9, 16, 25, 36, 49, 64, 81], result) def testProcsCtrlC(self): @@ -127,7 +127,7 @@ class ScopedPoolTest(GitCommonTestBase): with self.assertRaises(KeyboardInterrupt): with self.gc.ScopedPool() as pool: # Make sure this pool is interrupted in mid-swing - for i in pool.imap(slow_square, xrange(20)): + for i in pool.imap(slow_square, range(20)): if i > 32: os.kill(os.getpid(), self.CTRL_C) result.append(i) @@ -146,7 +146,6 @@ class ProgressPrinterTest(GitCommonTestBase): def flush(self): self.count += 1 - @unittest.expectedFailure def testBasic(self): """This test is probably racy, but I don't have a better alternative.""" fmt = '%(count)d/10' @@ -154,12 +153,12 @@ class ProgressPrinterTest(GitCommonTestBase): pp = self.gc.ProgressPrinter(fmt, enabled=True, fout=stream, period=0.01) with pp as inc: - for _ in xrange(10): + for _ in range(10): time.sleep(0.02) inc() filtered = {x.strip() for x in stream.data} - rslt = {fmt % {'count': i} for i in xrange(11)} + rslt = {fmt % {'count': i} for i in range(11)} self.assertSetEqual(filtered, rslt) self.assertGreaterEqual(stream.count, 10) @@ -172,24 +171,24 @@ class GitReadOnlyFunctionsTest(git_test_utils.GitRepoReadOnlyTestBase, """ COMMIT_A = { - 'some/files/file1': {'data': 'file1'}, - 'some/files/file2': {'data': 'file2'}, - 'some/files/file3': {'data': 'file3'}, - 'some/other/file': {'data': 'otherfile'}, + 'some/files/file1': {'data': b'file1'}, + 'some/files/file2': {'data': b'file2'}, + 'some/files/file3': {'data': b'file3'}, + 'some/other/file': {'data': b'otherfile'}, } COMMIT_C = { 'some/files/file2': { 'mode': 0o755, - 'data': 'file2 - vanilla\n'}, + 'data': b'file2 - vanilla\n'}, } COMMIT_E = { - 'some/files/file2': {'data': 'file2 - merged\n'}, + 'some/files/file2': {'data': b'file2 - merged\n'}, } COMMIT_D = { - 'some/files/file2': {'data': 'file2 - vanilla\nfile2 - merged\n'}, + 'some/files/file2': {'data': b'file2 - vanilla\nfile2 - merged\n'}, } def testHashes(self): @@ -209,7 +208,7 @@ class GitReadOnlyFunctionsTest(git_test_utils.GitRepoReadOnlyTestBase, self.repo['E'], self.repo['C'], ], ret) - self.assertEquals( + self.assertEqual( self.repo.run(self.gc.hash_one, 'branch_D'), self.repo['D'] ) @@ -217,23 +216,23 @@ class GitReadOnlyFunctionsTest(git_test_utils.GitRepoReadOnlyTestBase, self.repo.run(self.gc.hash_one, 'branch_D', short=True))) def testStream(self): - items = set(self.repo.commit_map.itervalues()) + items = set(self.repo.commit_map.values()) def testfn(): - for line in self.gc.run_stream('log', '--format=%H').xreadlines(): - line = line.strip() + for line in self.gc.run_stream('log', '--format=%H').readlines(): + line = line.strip().decode('utf-8') self.assertIn(line, items) items.remove(line) self.repo.run(testfn) def testStreamWithRetcode(self): - items = set(self.repo.commit_map.itervalues()) + items = set(self.repo.commit_map.values()) def testfn(): with self.gc.run_stream_with_retcode('log', '--format=%H') as stdout: - for line in stdout.xreadlines(): - line = line.strip() + for line in stdout.readlines(): + line = line.strip().decode('utf-8') self.assertIn(line, items) items.remove(line) @@ -326,13 +325,14 @@ class GitReadOnlyFunctionsTest(git_test_utils.GitRepoReadOnlyTestBase, 'tag_C^{}', ] ) - self.assertEqual(ret, map(binascii.unhexlify, [ - self.repo['D'], - self.repo['A'], - self.repo['B'], - self.repo['E'], - self.repo['C'], - ])) + hashes = [ + self.repo['D'], + self.repo['A'], + self.repo['B'], + self.repo['E'], + self.repo['C'], + ] + self.assertEqual(ret, [binascii.unhexlify(h) for h in hashes]) with self.assertRaisesRegexp(Exception, r"one of \('master', 'bananas'\)"): self.repo.run(self.gc.parse_commitrefs, 'master', 'bananas') @@ -356,20 +356,20 @@ class GitReadOnlyFunctionsTest(git_test_utils.GitRepoReadOnlyTestBase, file1 = self.COMMIT_A['some/files/file1']['data'] file2 = self.COMMIT_D['some/files/file2']['data'] file3 = self.COMMIT_A['some/files/file3']['data'] - self.assertEquals( + self.assertEqual( tree['file1'], ('100644', 'blob', git_test_utils.git_hash_data(file1))) - self.assertEquals( + self.assertEqual( tree['file2'], ('100755', 'blob', git_test_utils.git_hash_data(file2))) - self.assertEquals( + self.assertEqual( tree['file3'], ('100644', 'blob', git_test_utils.git_hash_data(file3))) tree = self.repo.run(self.gc.tree, 'master:some') - self.assertEquals(len(tree), 2) + self.assertEqual(len(tree), 2) # Don't check the tree hash because we're lazy :) - self.assertEquals(tree['files'][:2], ('040000', 'tree')) + self.assertEqual(tree['files'][:2], ('040000', 'tree')) tree = self.repo.run(self.gc.tree, 'master:wat') self.assertEqual(tree, None) @@ -380,16 +380,16 @@ class GitReadOnlyFunctionsTest(git_test_utils.GitRepoReadOnlyTestBase, file2 = self.COMMIT_D['some/files/file2']['data'] file3 = self.COMMIT_A['some/files/file3']['data'] other = self.COMMIT_A['some/other/file']['data'] - self.assertEquals( + self.assertEqual( tree['files/file1'], ('100644', 'blob', git_test_utils.git_hash_data(file1))) - self.assertEquals( + self.assertEqual( tree['files/file2'], ('100755', 'blob', git_test_utils.git_hash_data(file2))) - self.assertEquals( + self.assertEqual( tree['files/file3'], ('100644', 'blob', git_test_utils.git_hash_data(file3))) - self.assertEquals( + self.assertEqual( tree['other/file'], ('100644', 'blob', git_test_utils.git_hash_data(other))) @@ -399,7 +399,7 @@ class GitMutableFunctionsTest(git_test_utils.GitRepoReadWriteTestBase, REPO_SCHEMA = '' def _intern_data(self, data): - with tempfile.TemporaryFile() as f: + with tempfile.TemporaryFile('w') as f: f.write(data) f.seek(0) return self.repo.run(self.gc.intern_f, f) @@ -407,8 +407,8 @@ class GitMutableFunctionsTest(git_test_utils.GitRepoReadWriteTestBase, def testInternF(self): data = 'CoolBobcatsBro' data_hash = self._intern_data(data) - self.assertEquals(git_test_utils.git_hash_data(data), data_hash) - self.assertEquals(data, self.repo.git('cat-file', 'blob', data_hash).stdout) + self.assertEqual(git_test_utils.git_hash_data(data.encode()), data_hash) + self.assertEqual(data, self.repo.git('cat-file', 'blob', data_hash).stdout) def testMkTree(self): tree = {} @@ -416,47 +416,47 @@ class GitMutableFunctionsTest(git_test_utils.GitRepoReadWriteTestBase, name = 'file%d' % i tree[name] = ('100644', 'blob', self._intern_data(name)) tree_hash = self.repo.run(self.gc.mktree, tree) - self.assertEquals('37b61866d6e061c4ba478e7eb525be7b5752737d', tree_hash) + self.assertEqual('37b61866d6e061c4ba478e7eb525be7b5752737d', tree_hash) def testConfig(self): self.repo.git('config', '--add', 'happy.derpies', 'food') - self.assertEquals(self.repo.run(self.gc.get_config_list, 'happy.derpies'), - ['food']) - self.assertEquals(self.repo.run(self.gc.get_config_list, 'sad.derpies'), []) + self.assertEqual(self.repo.run(self.gc.get_config_list, 'happy.derpies'), + ['food']) + self.assertEqual(self.repo.run(self.gc.get_config_list, 'sad.derpies'), []) self.repo.git('config', '--add', 'happy.derpies', 'cat') - self.assertEquals(self.repo.run(self.gc.get_config_list, 'happy.derpies'), - ['food', 'cat']) + self.assertEqual(self.repo.run(self.gc.get_config_list, 'happy.derpies'), + ['food', 'cat']) - self.assertEquals('cat', self.repo.run(self.gc.get_config, 'dude.bob', - 'cat')) + self.assertEqual( + 'cat', self.repo.run(self.gc.get_config, 'dude.bob', 'cat')) self.repo.run(self.gc.set_config, 'dude.bob', 'dog') - self.assertEquals('dog', self.repo.run(self.gc.get_config, 'dude.bob', - 'cat')) + self.assertEqual( + 'dog', self.repo.run(self.gc.get_config, 'dude.bob', 'cat')) self.repo.run(self.gc.del_config, 'dude.bob') # This should work without raising an exception self.repo.run(self.gc.del_config, 'dude.bob') - self.assertEquals('cat', self.repo.run(self.gc.get_config, 'dude.bob', - 'cat')) + self.assertEqual( + 'cat', self.repo.run(self.gc.get_config, 'dude.bob', 'cat')) - self.assertEquals('origin/master', self.repo.run(self.gc.root)) + self.assertEqual('origin/master', self.repo.run(self.gc.root)) self.repo.git('config', 'depot-tools.upstream', 'catfood') - self.assertEquals('catfood', self.repo.run(self.gc.root)) + self.assertEqual('catfood', self.repo.run(self.gc.root)) def testUpstream(self): self.repo.git('commit', '--allow-empty', '-am', 'foooooo') - self.assertEquals(self.repo.run(self.gc.upstream, 'bobly'), None) - self.assertEquals(self.repo.run(self.gc.upstream, 'master'), None) + self.assertEqual(self.repo.run(self.gc.upstream, 'bobly'), None) + self.assertEqual(self.repo.run(self.gc.upstream, 'master'), None) self.repo.git('checkout', '-tb', 'happybranch', 'master') - self.assertEquals(self.repo.run(self.gc.upstream, 'happybranch'), - 'master') + self.assertEqual(self.repo.run(self.gc.upstream, 'happybranch'), + 'master') def testNormalizedVersion(self): self.assertTrue(all( @@ -505,7 +505,7 @@ class GitMutableFunctionsTest(git_test_utils.GitRepoReadWriteTestBase, ), 'to_delete': None } - self.assertEquals(expected, actual) + self.assertEqual(expected, actual) class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase, @@ -520,12 +520,12 @@ class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase, CAT DOG """ - COMMIT_B = {'file': {'data': 'B'}} - COMMIT_H = {'file': {'data': 'H'}} - COMMIT_I = {'file': {'data': 'I'}} - COMMIT_J = {'file': {'data': 'J'}} - COMMIT_K = {'file': {'data': 'K'}} - COMMIT_L = {'file': {'data': 'L'}} + COMMIT_B = {'file': {'data': b'B'}} + COMMIT_H = {'file': {'data': b'H'}} + COMMIT_I = {'file': {'data': b'I'}} + COMMIT_J = {'file': {'data': b'J'}} + COMMIT_K = {'file': {'data': b'K'}} + COMMIT_L = {'file': {'data': b'L'}} def setUp(self): super(GitMutableStructuredTest, self).setUp() @@ -536,7 +536,7 @@ class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase, self.repo.git('branch', '--set-upstream-to', 'root_X', 'root_A') def testTooManyBranches(self): - for i in xrange(30): + for i in range(30): self.repo.git('branch', 'a'*i) _, rslt = self.repo.capture_stdio(list, self.gc.branches()) @@ -597,8 +597,8 @@ class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase, self.repo.run(self.gc.remove_merge_base, 'branch_K') self.repo.run(self.gc.remove_merge_base, 'branch_L') - self.assertEqual(None, self.repo.run(self.gc.get_config, - 'branch.branch_K.base')) + self.assertEqual( + None, self.repo.run(self.gc.get_config, 'branch.branch_K.base')) self.assertEqual({}, self.repo.run(self.gc.branch_config_map, 'base')) @@ -658,20 +658,20 @@ class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase, self.repo.capture_stdio( lambda: retval.append(self.repo.run(self.gc.is_dirty_git_tree, 'foo'))) - self.assertEquals(False, retval[0]) + self.assertEqual(False, retval[0]) self.repo.open('test.file', 'w').write('test data') self.repo.git('add', 'test.file') retval = [] self.repo.capture_stdio( lambda: retval.append(self.repo.run(self.gc.is_dirty_git_tree, 'foo'))) - self.assertEquals(True, retval[0]) + self.assertEqual(True, retval[0]) def testSquashBranch(self): self.repo.git('checkout', 'branch_K') - self.assertEquals(True, self.repo.run(self.gc.squash_current_branch, - 'cool message')) + self.assertEqual( + True, self.repo.run(self.gc.squash_current_branch, 'cool message')) lines = ['cool message', ''] for l in 'HIJK': @@ -679,18 +679,18 @@ class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase, lines.pop() msg = '\n'.join(lines) - self.assertEquals(self.repo.run(self.gc.run, 'log', '-n1', '--format=%B'), - msg) + self.assertEqual(self.repo.run(self.gc.run, 'log', '-n1', '--format=%B'), + msg) - self.assertEquals( + self.assertEqual( self.repo.git('cat-file', 'blob', 'branch_K:file').stdout, 'K' ) def testSquashBranchDefaultMessage(self): self.repo.git('checkout', 'branch_K') - self.assertEquals(True, self.repo.run(self.gc.squash_current_branch)) - self.assertEquals(self.repo.run(self.gc.run, 'log', '-n1', '--format=%s'), + self.assertEqual(True, self.repo.run(self.gc.squash_current_branch)) + self.assertEqual(self.repo.run(self.gc.run, 'log', '-n1', '--format=%s'), 'git squash commit for branch_K.') def testSquashBranchEmpty(self): @@ -699,7 +699,7 @@ class GitMutableStructuredTest(git_test_utils.GitRepoReadWriteTestBase, self.repo.git('commit', '-m', 'revert all changes no branch') # Should return False since the quash would result in an empty commit stdout = self.repo.capture_stdio(self.gc.squash_current_branch)[0] - self.assertEquals(stdout, 'Nothing to commit; squashed branch is empty\n') + self.assertEqual(stdout, 'Nothing to commit; squashed branch is empty\n') def testRebase(self): self.assertSchema(""" @@ -782,24 +782,24 @@ class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase): """ COMMIT_A = { - 'some/files/file1': {'data': 'file1'}, - 'some/files/file2': {'data': 'file2'}, - 'some/files/file3': {'data': 'file3'}, - 'some/other/file': {'data': 'otherfile'}, + 'some/files/file1': {'data': b'file1'}, + 'some/files/file2': {'data': b'file2'}, + 'some/files/file3': {'data': b'file3'}, + 'some/other/file': {'data': b'otherfile'}, } COMMIT_C = { 'some/files/file2': { 'mode': 0o755, - 'data': 'file2 - vanilla'}, + 'data': b'file2 - vanilla'}, } COMMIT_E = { - 'some/files/file2': {'data': 'file2 - merged'}, + 'some/files/file2': {'data': b'file2 - merged'}, } COMMIT_D = { - 'some/files/file2': {'data': 'file2 - vanilla\nfile2 - merged'}, + 'some/files/file2': {'data': b'file2 - vanilla\nfile2 - merged'}, } def testNothing(self): @@ -825,23 +825,23 @@ class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase): self.repo.git('add', 'some/files/file5') # Freeze group 1 - self.assertEquals(self.repo.git('status', '--porcelain').stdout, STATUS_1) + self.assertEqual(self.repo.git('status', '--porcelain').stdout, STATUS_1) self.assertIsNone(self.gc.freeze()) - self.assertEquals(self.repo.git('status', '--porcelain').stdout, '') + self.assertEqual(self.repo.git('status', '--porcelain').stdout, '') # Freeze group 2 with open('some/files/file2', 'a') as f2: print('new! appended line!', file=f2) - self.assertEquals(self.repo.git('status', '--porcelain').stdout, - ' M some/files/file2\n') + self.assertEqual(self.repo.git('status', '--porcelain').stdout, + ' M some/files/file2\n') self.assertIsNone(self.gc.freeze()) - self.assertEquals(self.repo.git('status', '--porcelain').stdout, '') + self.assertEqual(self.repo.git('status', '--porcelain').stdout, '') # Thaw it out! self.assertIsNone(self.gc.thaw()) self.assertIsNotNone(self.gc.thaw()) # One thaw should thaw everything - self.assertEquals(self.repo.git('status', '--porcelain').stdout, STATUS_1) + self.assertEqual(self.repo.git('status', '--porcelain').stdout, STATUS_1) self.repo.run(inner) @@ -850,7 +850,7 @@ class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase): self.repo.git('config', 'depot-tools.freeze-size-limit', '1') with open('bigfile', 'w') as f: chunk = 'NERDFACE' * 1024 - for _ in xrange(128 * 2 + 1): # Just over 2 mb + for _ in range(128 * 2 + 1): # Just over 2 mb f.write(chunk) _, err = self.repo.capture_stdio(self.gc.freeze) self.assertIn('too much untracked+unignored', err) @@ -860,10 +860,10 @@ class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase): def testTooBigMultipleFiles(self): def inner(): self.repo.git('config', 'depot-tools.freeze-size-limit', '1') - for i in xrange(3): + for i in range(3): with open('file%d' % i, 'w') as f: chunk = 'NERDFACE' * 1024 - for _ in xrange(50): # About 400k + for _ in range(50): # About 400k f.write(chunk) _, err = self.repo.capture_stdio(self.gc.freeze) self.assertIn('too much untracked+unignored', err) @@ -896,7 +896,7 @@ class GitFreezeThaw(git_test_utils.GitRepoReadWriteTestBase): self.repo.git('checkout', '-b', 'unreadable_file_branch') with open('bad_file', 'w') as f: f.write('some text') - os.chmod('bad_file', 0111) + os.chmod('bad_file', 0o0111) ret = self.repo.run(self.gc.freeze) self.assertIn('Failed to index some unindexed files.', ret) @@ -935,11 +935,11 @@ class GitTestUtilsTest(git_test_utils.GitRepoReadOnlyTestBase): """ COMMIT_A = { - 'file1': {'data': 'file1'}, + 'file1': {'data': b'file1'}, } COMMIT_B = { - 'file1': {'data': 'file1 changed'}, + 'file1': {'data': b'file1 changed'}, } # Test special keys (custom commit data). @@ -952,7 +952,7 @@ class GitTestUtilsTest(git_test_utils.GitRepoReadOnlyTestBase): GitRepo.COMMITTER_EMAIL: 'committer@example.com', GitRepo.COMMITTER_DATE: datetime.datetime(1990, 4, 5, 6, 7, 8, tzinfo=git_test_utils.UTC), - 'file1': {'data': 'file1 changed again'}, + 'file1': {'data': b'file1 changed again'}, } def testAutomaticCommitDates(self): @@ -960,22 +960,22 @@ class GitTestUtilsTest(git_test_utils.GitRepoReadOnlyTestBase): # must be in UTC (otherwise the tests are system-dependent, and if your # local timezone is positive, timestamps will be <0 which causes bizarre # behaviour in Git; http://crbug.com/581895). - self.assertEquals('Author McAuthorly 1970-01-01 00:00:00 +0000', - self.repo.show_commit('A', format_string='%an %ai')) - self.assertEquals('Charles Committish 1970-01-02 00:00:00 +0000', - self.repo.show_commit('A', format_string='%cn %ci')) - self.assertEquals('Author McAuthorly 1970-01-03 00:00:00 +0000', - self.repo.show_commit('B', format_string='%an %ai')) - self.assertEquals('Charles Committish 1970-01-04 00:00:00 +0000', - self.repo.show_commit('B', format_string='%cn %ci')) + self.assertEqual('Author McAuthorly 1970-01-01 00:00:00 +0000', + self.repo.show_commit('A', format_string='%an %ai')) + self.assertEqual('Charles Committish 1970-01-02 00:00:00 +0000', + self.repo.show_commit('A', format_string='%cn %ci')) + self.assertEqual('Author McAuthorly 1970-01-03 00:00:00 +0000', + self.repo.show_commit('B', format_string='%an %ai')) + self.assertEqual('Charles Committish 1970-01-04 00:00:00 +0000', + self.repo.show_commit('B', format_string='%cn %ci')) def testCustomCommitData(self): - self.assertEquals('Custom Author author@example.com ' - '1980-09-08 07:06:05 +0000', - self.repo.show_commit('C', format_string='%an %ae %ai')) - self.assertEquals('Custom Committer committer@example.com ' - '1990-04-05 06:07:08 +0000', - self.repo.show_commit('C', format_string='%cn %ce %ci')) + self.assertEqual('Custom Author author@example.com ' + '1980-09-08 07:06:05 +0000', + self.repo.show_commit('C', format_string='%an %ae %ai')) + self.assertEqual('Custom Committer committer@example.com ' + '1990-04-05 06:07:08 +0000', + self.repo.show_commit('C', format_string='%cn %ce %ci')) if __name__ == '__main__': sys.exit(coverage_utils.covered_main( diff --git a/tests/git_hyper_blame_test.py b/tests/git_hyper_blame_test.py index 624648954f..584813e60e 100755 --- a/tests/git_hyper_blame_test.py +++ b/tests/git_hyper_blame_test.py @@ -4,15 +4,21 @@ # found in the LICENSE file. """Tests for git_dates.""" +from __future__ import unicode_literals + import datetime import os import re import shutil -import StringIO import sys import tempfile import unittest +if sys.version_info.major == 2: + from StringIO import StringIO +else: + from io import StringIO + DEPOT_TOOLS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, DEPOT_TOOLS_ROOT) @@ -32,8 +38,8 @@ class GitHyperBlameTestBase(git_test_utils.GitRepoReadOnlyTestBase): cls.git_hyper_blame = git_hyper_blame def run_hyperblame(self, ignored, filename, revision): - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() ignored = [self.repo[c] for c in ignored] retval = self.repo.run(self.git_hyper_blame.hyper_blame, ignored, filename, revision=revision, out=stdout, err=stderr) @@ -61,22 +67,22 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): REPO_SCHEMA = "A B C D" COMMIT_A = { - 'some/files/file': {'data': 'line 1\nline 2\n'}, + 'some/files/file': {'data': b'line 1\nline 2\n'}, } COMMIT_B = { - 'some/files/file': {'data': 'line 1\nline 2.1\n'}, + 'some/files/file': {'data': b'line 1\nline 2.1\n'}, } COMMIT_C = { - 'some/files/file': {'data': 'line 1.1\nline 2.1\n'}, + 'some/files/file': {'data': b'line 1.1\nline 2.1\n'}, } COMMIT_D = { # This file should be automatically considered for ignore. - '.git-blame-ignore-revs': {'data': 'tag_C'}, + '.git-blame-ignore-revs': {'data': b'tag_C'}, # This file should not be considered. - 'some/files/.git-blame-ignore-revs': {'data': 'tag_B'}, + 'some/files/.git-blame-ignore-revs': {'data': b'tag_B'}, } def setUp(self): @@ -89,8 +95,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): """Tests the main function (simple end-to-end test with no ignores).""" expected_output = [self.blame_line('C', '1) line 1.1'), self.blame_line('B', '2) line 2.1')] - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() retval = self.repo.run(self.git_hyper_blame.main, args=['tag_C', 'some/files/file'], stdout=stdout, stderr=stderr) @@ -102,8 +108,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): """Tests the main function (simple end-to-end test with ignores).""" expected_output = [self.blame_line('C', ' 1) line 1.1'), self.blame_line('A', '2*) line 2.1')] - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() retval = self.repo.run(self.git_hyper_blame.main, args=['-i', 'tag_B', 'tag_C', 'some/files/file'], stdout=stdout, stderr=stderr) @@ -118,8 +124,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): tempdir = tempfile.mkdtemp(suffix='_nogit', prefix='git_repo') try: os.chdir(tempdir) - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() retval = self.git_hyper_blame.main( args=['-i', 'tag_B', 'tag_C', 'some/files/file'], stdout=stdout, stderr=stderr) @@ -134,8 +140,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): def testBadFilename(self): """Tests the main function (bad filename).""" - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() retval = self.repo.run(self.git_hyper_blame.main, args=['-i', 'tag_B', 'tag_C', 'some/files/xxxx'], stdout=stdout, stderr=stderr) @@ -150,8 +156,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): def testBadRevision(self): """Tests the main function (bad revision to blame from).""" - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() retval = self.repo.run(self.git_hyper_blame.main, args=['-i', 'tag_B', 'xxxx', 'some/files/file'], stdout=stdout, stderr=stderr) @@ -165,8 +171,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): """Tests the main function (bad revision passed to -i).""" expected_output = [self.blame_line('C', '1) line 1.1'), self.blame_line('B', '2) line 2.1')] - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() retval = self.repo.run(self.git_hyper_blame.main, args=['-i', 'xxxx', 'tag_C', 'some/files/file'], stdout=stdout, stderr=stderr) @@ -178,8 +184,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): """Tests passing the ignore list in a file.""" expected_output = [self.blame_line('C', ' 1) line 1.1'), self.blame_line('A', '2*) line 2.1')] - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() with tempfile.NamedTemporaryFile(mode='w+', prefix='ignore') as ignore_file: ignore_file.write('# Line comments are allowed.\n') @@ -205,8 +211,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): expected_output = [self.blame_line('A', '1*) line 1.1'), self.blame_line('B', ' 2) line 2.1')] - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() retval = self.repo.run(self.git_hyper_blame.main, args=['tag_D', 'some/files/file'], @@ -219,8 +225,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): # Test blame from a different revision. Despite the default ignore file # *not* being committed at that revision, it should still be picked up # because D is currently checked out. - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() retval = self.repo.run(self.git_hyper_blame.main, args=['tag_C', 'some/files/file'], @@ -238,8 +244,8 @@ class GitHyperBlameMainTest(GitHyperBlameTestBase): expected_output = [self.blame_line('C', '1) line 1.1'), self.blame_line('B', '2) line 2.1')] - stdout = StringIO.StringIO() - stderr = StringIO.StringIO() + stdout = StringIO() + stderr = StringIO() retval = self.repo.run( self.git_hyper_blame.main, @@ -257,44 +263,44 @@ class GitHyperBlameSimpleTest(GitHyperBlameTestBase): """ COMMIT_A = { - 'some/files/file1': {'data': 'file1'}, - 'some/files/file2': {'data': 'file2'}, - 'some/files/empty': {'data': ''}, - 'some/other/file': {'data': 'otherfile'}, + 'some/files/file1': {'data': b'file1'}, + 'some/files/file2': {'data': b'file2'}, + 'some/files/empty': {'data': b''}, + 'some/other/file': {'data': b'otherfile'}, } COMMIT_B = { 'some/files/file2': { 'mode': 0o755, - 'data': 'file2 - vanilla\n'}, - 'some/files/empty': {'data': 'not anymore'}, - 'some/files/file3': {'data': 'file3'}, + 'data': b'file2 - vanilla\n'}, + 'some/files/empty': {'data': b'not anymore'}, + 'some/files/file3': {'data': b'file3'}, } COMMIT_C = { - 'some/files/file2': {'data': 'file2 - merged\n'}, + 'some/files/file2': {'data': b'file2 - merged\n'}, } COMMIT_D = { - 'some/files/file2': {'data': 'file2 - vanilla\nfile2 - merged\n'}, + 'some/files/file2': {'data': b'file2 - vanilla\nfile2 - merged\n'}, } COMMIT_E = { - 'some/files/file2': {'data': 'file2 - vanilla\nfile_x - merged\n'}, + 'some/files/file2': {'data': b'file2 - vanilla\nfile_x - merged\n'}, } COMMIT_F = { - 'some/files/file2': {'data': 'file2 - vanilla\nfile_y - merged\n'}, + 'some/files/file2': {'data': b'file2 - vanilla\nfile_y - merged\n'}, } # Move file2 from files to other. COMMIT_G = { 'some/files/file2': {'data': None}, - 'some/other/file2': {'data': 'file2 - vanilla\nfile_y - merged\n'}, + 'some/other/file2': {'data': b'file2 - vanilla\nfile_y - merged\n'}, } COMMIT_H = { - 'some/other/file2': {'data': 'file2 - vanilla\nfile_z - merged\n'}, + 'some/other/file2': {'data': b'file2 - vanilla\nfile_z - merged\n'}, } def testBlameError(self): @@ -387,34 +393,34 @@ class GitHyperBlameLineMotionTest(GitHyperBlameTestBase): """ COMMIT_A = { - 'file': {'data': 'A\ngreen\nblue\n'}, + 'file': {'data': b'A\ngreen\nblue\n'}, } # Change "green" to "yellow". COMMIT_B = { - 'file': {'data': 'A\nyellow\nblue\n'}, + 'file': {'data': b'A\nyellow\nblue\n'}, } # Insert 2 lines at the top, # Change "yellow" to "red". # Insert 1 line at the bottom. COMMIT_C = { - 'file': {'data': 'X\nY\nA\nred\nblue\nZ\n'}, + 'file': {'data': b'X\nY\nA\nred\nblue\nZ\n'}, } # Insert 2 more lines at the top. COMMIT_D = { - 'file': {'data': 'earth\nfire\nX\nY\nA\nred\nblue\nZ\n'}, + 'file': {'data': b'earth\nfire\nX\nY\nA\nred\nblue\nZ\n'}, } # Insert a line before "red", and indent "red" and "blue". COMMIT_E = { - 'file': {'data': 'earth\nfire\nX\nY\nA\ncolors:\n red\n blue\nZ\n'}, + 'file': {'data': b'earth\nfire\nX\nY\nA\ncolors:\n red\n blue\nZ\n'}, } # Insert a line between "A" and "colors". COMMIT_F = { - 'file': {'data': 'earth\nfire\nX\nY\nA\nB\ncolors:\n red\n blue\nZ\n'}, + 'file': {'data': b'earth\nfire\nX\nY\nA\nB\ncolors:\n red\n blue\nZ\n'}, } def testCacheDiffHunks(self): @@ -537,22 +543,22 @@ class GitHyperBlameLineNumberTest(GitHyperBlameTestBase): """ COMMIT_A = { - 'file': {'data': 'red\nblue\n'}, + 'file': {'data': b'red\nblue\n'}, } # Change "blue" to "green". COMMIT_B = { - 'file': {'data': 'red\ngreen\n'}, + 'file': {'data': b'red\ngreen\n'}, } # Insert 2 lines at the top, COMMIT_C = { - 'file': {'data': '\n\nred\ngreen\n'}, + 'file': {'data': b'\n\nred\ngreen\n'}, } # Change "green" to "yellow". COMMIT_D = { - 'file': {'data': '\n\nred\nyellow\n'}, + 'file': {'data': b'\n\nred\nyellow\n'}, } def testTwoChangesWithAddedLines(self): @@ -584,19 +590,19 @@ class GitHyperBlameUnicodeTest(GitHyperBlameTestBase): COMMIT_A = { GitRepo.AUTHOR_NAME: 'ASCII Author', - 'file': {'data': 'red\nblue\n'}, + 'file': {'data': b'red\nblue\n'}, } # Add a line. COMMIT_B = { - GitRepo.AUTHOR_NAME: u'\u4e2d\u56fd\u4f5c\u8005'.encode('utf-8'), - 'file': {'data': 'red\ngreen\nblue\n'}, + GitRepo.AUTHOR_NAME: '\u4e2d\u56fd\u4f5c\u8005'.encode('utf-8'), + 'file': {'data': b'red\ngreen\nblue\n'}, } # Modify a line with non-UTF-8 author and file text. COMMIT_C = { - GitRepo.AUTHOR_NAME: u'Lat\u00edn-1 Author'.encode('latin-1'), - 'file': {'data': u'red\ngre\u00e9n\nblue\n'.encode('latin-1')}, + GitRepo.AUTHOR_NAME: 'Lat\u00edn-1 Author'.encode('latin-1'), + 'file': {'data': 'red\ngre\u00e9n\nblue\n'.encode('latin-1')}, } def testNonASCIIAuthorName(self): @@ -610,8 +616,8 @@ class GitHyperBlameUnicodeTest(GitHyperBlameTestBase): expected_output = [ self.blame_line('A', '1) red', author='ASCII Author'), # Expect 8 spaces, to line up with the other name. - self.blame_line('B', '2) green', - author=u'\u4e2d\u56fd\u4f5c\u8005 '.encode('utf-8')), + self.blame_line( + 'B', '2) green', author='\u4e2d\u56fd\u4f5c\u8005 '), self.blame_line('A', '3) blue', author='ASCII Author'), ] retval, output = self.run_hyperblame([], 'file', 'tag_B') @@ -626,9 +632,9 @@ class GitHyperBlameUnicodeTest(GitHyperBlameTestBase): """ expected_output = [ self.blame_line('A', '1) red', author='ASCII Author '), - # The Author has been re-encoded as UTF-8. The file data is preserved as - # raw byte data. - self.blame_line('C', '2) gre\xe9n', author='Lat\xc3\xadn-1 Author'), + # The Author has been re-encoded as UTF-8. The file data is converted to + # UTF8 and unknown characters replaced. + self.blame_line('C', '2) gre\ufffdn', author='Lat\xedn-1 Author'), self.blame_line('A', '3) blue', author='ASCII Author '), ] retval, output = self.run_hyperblame([], 'file', 'tag_C')