comparison tests/run-tests.py @ 25161:4d30467d944e

run-tests: move unicode-to-bytes operations on paths to a helper (issue4667) This doesn't fix the probably-wrong utf-8 encoding choice, it just starts the process of encapsulating all the path handling in run-tests in a single place. One known-path use of .encode() remains: it's related to use of mkdtemp, and it will be fixed in a followup patch once we have a companion _strpath() helper function to go from bytes to a str, as we need to file a bug about mkdtemp upstream.
author Augie Fackler <augie@google.com>
date Sun, 17 May 2015 21:40:12 -0400
parents fefc72523491
children 153b9c5235c2
comparison
equal deleted inserted replaced
25160:fefc72523491 25161:4d30467d944e
79 processlock = threading.Lock() 79 processlock = threading.Lock()
80 80
81 if sys.version_info > (3, 5, 0): 81 if sys.version_info > (3, 5, 0):
82 PYTHON3 = True 82 PYTHON3 = True
83 xrange = range # we use xrange in one place, and we'd rather not use range 83 xrange = range # we use xrange in one place, and we'd rather not use range
84 def _bytespath(p):
85 return p.encode('utf-8')
84 elif sys.version_info >= (3, 0, 0): 86 elif sys.version_info >= (3, 0, 0):
85 print('%s is only supported on Python 3.5+ and 2.6-2.7, not %s' % 87 print('%s is only supported on Python 3.5+ and 2.6-2.7, not %s' %
86 (sys.argv[0], '.'.join(str(v) for v in sys.version_info[:3]))) 88 (sys.argv[0], '.'.join(str(v) for v in sys.version_info[:3])))
87 sys.exit(70) # EX_SOFTWARE from `man 3 sysexit` 89 sys.exit(70) # EX_SOFTWARE from `man 3 sysexit`
88 else: 90 else:
89 PYTHON3 = False 91 PYTHON3 = False
92 def _bytespath(p):
93 return p
90 94
91 def checkportisavailable(port): 95 def checkportisavailable(port):
92 """return true if a port seems free to bind on localhost""" 96 """return true if a port seems free to bind on localhost"""
93 try: 97 try:
94 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 98 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
124 terminate(p) 128 terminate(p)
125 threading.Thread(target=t).start() 129 threading.Thread(target=t).start()
126 130
127 return p 131 return p
128 132
129 PYTHON = sys.executable.replace('\\', '/').encode('utf-8') 133 PYTHON = _bytespath(sys.executable.replace('\\', '/'))
130 IMPL_PATH = b'PYTHONPATH' 134 IMPL_PATH = b'PYTHONPATH'
131 if 'java' in sys.platform: 135 if 'java' in sys.platform:
132 IMPL_PATH = b'JYTHONPATH' 136 IMPL_PATH = b'JYTHONPATH'
133 137
134 defaults = { 138 defaults = {
262 os.access(options.with_hg, os.X_OK)): 266 os.access(options.with_hg, os.X_OK)):
263 parser.error('--with-hg must specify an executable hg script') 267 parser.error('--with-hg must specify an executable hg script')
264 if not os.path.basename(options.with_hg) == 'hg': 268 if not os.path.basename(options.with_hg) == 'hg':
265 sys.stderr.write('warning: --with-hg should specify an hg script\n') 269 sys.stderr.write('warning: --with-hg should specify an hg script\n')
266 if options.local: 270 if options.local:
267 testdir = os.path.dirname(os.path.realpath(sys.argv[0]).encode('utf-8')) 271 testdir = os.path.dirname(_bytespath(os.path.realpath(sys.argv[0])))
268 hgbin = os.path.join(os.path.dirname(testdir), b'hg') 272 hgbin = os.path.join(os.path.dirname(testdir), b'hg')
269 if os.name != 'nt' and not os.access(hgbin, os.X_OK): 273 if os.name != 'nt' and not os.access(hgbin, os.X_OK):
270 parser.error('--local specified, but %r not found or not executable' 274 parser.error('--local specified, but %r not found or not executable'
271 % hgbin) 275 % hgbin)
272 options.with_hg = hgbin 276 options.with_hg = hgbin
448 self._debug = debug 452 self._debug = debug
449 self._timeout = timeout 453 self._timeout = timeout
450 self._startport = startport 454 self._startport = startport
451 self._extraconfigopts = extraconfigopts or [] 455 self._extraconfigopts = extraconfigopts or []
452 self._py3kwarnings = py3kwarnings 456 self._py3kwarnings = py3kwarnings
453 self._shell = shell.encode('utf-8') 457 self._shell = _bytespath(shell)
454 458
455 self._aborted = False 459 self._aborted = False
456 self._daemonpids = [] 460 self._daemonpids = []
457 self._finished = None 461 self._finished = None
458 self._ret = None 462 self._ret = None
1281 if self._options.nodiff: 1285 if self._options.nodiff:
1282 pass 1286 pass
1283 elif self._options.view: 1287 elif self._options.view:
1284 v = self._options.view 1288 v = self._options.view
1285 if PYTHON3: 1289 if PYTHON3:
1286 v = v.encode('utf-8') 1290 v = _bytespath(v)
1287 os.system(b"%s %s %s" % 1291 os.system(b"%s %s %s" %
1288 (v, test.refpath, test.errpath)) 1292 (v, test.refpath, test.errpath))
1289 else: 1293 else:
1290 servefail, lines = getdiff(expected, got, 1294 servefail, lines = getdiff(expected, got,
1291 test.refpath, test.errpath) 1295 test.refpath, test.errpath)
1619 Tests rely on a lot of state. This object holds it for them. 1623 Tests rely on a lot of state. This object holds it for them.
1620 """ 1624 """
1621 1625
1622 # Programs required to run tests. 1626 # Programs required to run tests.
1623 REQUIREDTOOLS = [ 1627 REQUIREDTOOLS = [
1624 os.path.basename(sys.executable).encode('utf-8'), 1628 os.path.basename(_bytespath(sys.executable)),
1625 b'diff', 1629 b'diff',
1626 b'grep', 1630 b'grep',
1627 b'unzip', 1631 b'unzip',
1628 b'gunzip', 1632 b'gunzip',
1629 b'bunzip2', 1633 b'bunzip2',
1655 """Run the test suite.""" 1659 """Run the test suite."""
1656 oldmask = os.umask(0o22) 1660 oldmask = os.umask(0o22)
1657 try: 1661 try:
1658 parser = parser or getparser() 1662 parser = parser or getparser()
1659 options, args = parseargs(args, parser) 1663 options, args = parseargs(args, parser)
1660 args = [a.encode('utf-8') for a in args] 1664 # positional arguments are paths to test files to run, so
1665 # we make sure they're all bytestrings
1666 args = [_bytespath(a) for a in args]
1661 self.options = options 1667 self.options = options
1662 1668
1663 self._checktools() 1669 self._checktools()
1664 tests = self.findtests(args) 1670 tests = self.findtests(args)
1665 if options.profile_runner: 1671 if options.profile_runner:
1705 # we do the randomness ourself to know what seed is used 1711 # we do the randomness ourself to know what seed is used
1706 os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32)) 1712 os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32))
1707 1713
1708 if self.options.tmpdir: 1714 if self.options.tmpdir:
1709 self.options.keep_tmpdir = True 1715 self.options.keep_tmpdir = True
1710 tmpdir = self.options.tmpdir.encode('utf-8') 1716 tmpdir = _bytespath(self.options.tmpdir)
1711 if os.path.exists(tmpdir): 1717 if os.path.exists(tmpdir):
1712 # Meaning of tmpdir has changed since 1.3: we used to create 1718 # Meaning of tmpdir has changed since 1.3: we used to create
1713 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if 1719 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
1714 # tmpdir already exists. 1720 # tmpdir already exists.
1715 print("error: temp dir %r already exists" % tmpdir) 1721 print("error: temp dir %r already exists" % tmpdir)
1740 # If --with-hg is not specified, we have bytes already, 1746 # If --with-hg is not specified, we have bytes already,
1741 # but if it was specified in python3 we get a str, so we 1747 # but if it was specified in python3 we get a str, so we
1742 # have to encode it back into a bytes. 1748 # have to encode it back into a bytes.
1743 if PYTHON3: 1749 if PYTHON3:
1744 if not isinstance(whg, bytes): 1750 if not isinstance(whg, bytes):
1745 whg = whg.encode('utf-8') 1751 whg = _bytespath(whg)
1746 self._bindir = os.path.dirname(os.path.realpath(whg)) 1752 self._bindir = os.path.dirname(os.path.realpath(whg))
1747 assert isinstance(self._bindir, bytes) 1753 assert isinstance(self._bindir, bytes)
1748 self._tmpbindir = os.path.join(self._hgtmp, b'install', b'bin') 1754 self._tmpbindir = os.path.join(self._hgtmp, b'install', b'bin')
1749 os.makedirs(self._tmpbindir) 1755 os.makedirs(self._tmpbindir)
1750 1756
1762 self._pythondir = os.path.join(self._installdir, b"lib", b"python") 1768 self._pythondir = os.path.join(self._installdir, b"lib", b"python")
1763 1769
1764 osenvironb[b"BINDIR"] = self._bindir 1770 osenvironb[b"BINDIR"] = self._bindir
1765 osenvironb[b"PYTHON"] = PYTHON 1771 osenvironb[b"PYTHON"] = PYTHON
1766 1772
1767 fileb = __file__.encode('utf-8') 1773 fileb = _bytespath(__file__)
1768 runtestdir = os.path.abspath(os.path.dirname(fileb)) 1774 runtestdir = os.path.abspath(os.path.dirname(fileb))
1769 if PYTHON3: 1775 if PYTHON3:
1770 sepb = os.pathsep.encode('utf-8') 1776 sepb = _bytespath(os.pathsep)
1771 else: 1777 else:
1772 sepb = os.pathsep 1778 sepb = os.pathsep
1773 path = [self._bindir, runtestdir] + osenvironb[b"PATH"].split(sepb) 1779 path = [self._bindir, runtestdir] + osenvironb[b"PATH"].split(sepb)
1774 if os.path.islink(__file__): 1780 if os.path.islink(__file__):
1775 # test helper will likely be at the end of the symlink 1781 # test helper will likely be at the end of the symlink
1999 # Run installer in hg root 2005 # Run installer in hg root
2000 script = os.path.realpath(sys.argv[0]) 2006 script = os.path.realpath(sys.argv[0])
2001 exe = sys.executable 2007 exe = sys.executable
2002 if PYTHON3: 2008 if PYTHON3:
2003 py3 = b'--c2to3' 2009 py3 = b'--c2to3'
2004 compiler = compiler.encode('utf-8') 2010 compiler = _bytespath(compiler)
2005 script = script.encode('utf-8') 2011 script = _bytespath(script)
2006 exe = exe.encode('utf-8') 2012 exe = _bytespath(exe)
2007 hgroot = os.path.dirname(os.path.dirname(script)) 2013 hgroot = os.path.dirname(os.path.dirname(script))
2008 self._hgroot = hgroot 2014 self._hgroot = hgroot
2009 os.chdir(hgroot) 2015 os.chdir(hgroot)
2010 nohome = b'--home=""' 2016 nohome = b'--home=""'
2011 if os.name == 'nt': 2017 if os.name == 'nt':
2121 cmd = cmd % PYTHON 2127 cmd = cmd % PYTHON
2122 if PYTHON3: 2128 if PYTHON3:
2123 cmd = cmd.decode('utf-8') 2129 cmd = cmd.decode('utf-8')
2124 pipe = os.popen(cmd) 2130 pipe = os.popen(cmd)
2125 try: 2131 try:
2126 self._hgpath = pipe.read().strip() 2132 self._hgpath = _bytespath(pipe.read().strip())
2127 if PYTHON3:
2128 self._hgpath = self._hgpath.encode('utf-8')
2129 finally: 2133 finally:
2130 pipe.close() 2134 pipe.close()
2131 2135
2132 return self._hgpath 2136 return self._hgpath
2133 2137
2159 os.mkdir(adir) 2163 os.mkdir(adir)
2160 cov.annotate(directory=adir, omit=omit) 2164 cov.annotate(directory=adir, omit=omit)
2161 2165
2162 def _findprogram(self, program): 2166 def _findprogram(self, program):
2163 """Search PATH for a executable program""" 2167 """Search PATH for a executable program"""
2164 if PYTHON3: 2168 dpb = _bytespath(os.defpath)
2165 dpb = os.defpath.encode('utf-8') 2169 sepb = _bytespath(os.pathsep)
2166 sepb = os.pathsep.encode('utf-8')
2167 else:
2168 dpb = os.defpath
2169 sepb = os.pathsep
2170 for p in osenvironb.get(b'PATH', dpb).split(sepb): 2170 for p in osenvironb.get(b'PATH', dpb).split(sepb):
2171 name = os.path.join(p, program) 2171 name = os.path.join(p, program)
2172 if os.name == 'nt' or os.access(name, os.X_OK): 2172 if os.name == 'nt' or os.access(name, os.X_OK):
2173 return name 2173 return name
2174 return None 2174 return None