run-tests: even more bytestring annotations for Python 3
authorAugie Fackler <augie@google.com>
Tue, 14 Apr 2015 16:03:04 -0400
changeset 25041 09c71e3da704
parent 25040 6b8c48cfb85e
child 25042 201823c50610
run-tests: even more bytestring annotations for Python 3
tests/run-tests.py
--- a/tests/run-tests.py	Tue Apr 14 16:02:49 2015 -0400
+++ b/tests/run-tests.py	Tue Apr 14 16:03:04 2015 -0400
@@ -128,9 +128,9 @@
     return p
 
 PYTHON = sys.executable.replace('\\', '/')
-IMPL_PATH = 'PYTHONPATH'
+IMPL_PATH = b'PYTHONPATH'
 if 'java' in sys.platform:
-    IMPL_PATH = 'JYTHONPATH'
+    IMPL_PATH = b'JYTHONPATH'
 
 defaults = {
     'jobs': ('HGTEST_JOBS', 1),
@@ -153,7 +153,7 @@
             continue
 
         for line in f.readlines():
-            line = line.split('#', 1)[0].strip()
+            line = line.split(b'#', 1)[0].strip()
             if line:
                 entries[line] = filename
 
@@ -263,8 +263,8 @@
         if not os.path.basename(options.with_hg) == 'hg':
             sys.stderr.write('warning: --with-hg should specify an hg script\n')
     if options.local:
-        testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
-        hgbin = os.path.join(os.path.dirname(testdir), 'hg')
+        testdir = os.path.dirname(os.path.realpath(sys.argv[0]).encode('utf-8'))
+        hgbin = os.path.join(os.path.dirname(testdir), b'hg')
         if os.name != 'nt' and not os.access(hgbin, os.X_OK):
             parser.error('--local specified, but %r not found or not executable'
                          % hgbin)
@@ -666,20 +666,20 @@
         occur.
         """
         r = [
-            (r':%s\b' % self._startport, ':$HGPORT'),
-            (r':%s\b' % (self._startport + 1), ':$HGPORT1'),
-            (r':%s\b' % (self._startport + 2), ':$HGPORT2'),
-            (r'(?m)^(saved backup bundle to .*\.hg)( \(glob\))?$',
-             r'\1 (glob)'),
+            (br':%d\b' % self._startport, b':$HGPORT'),
+            (br':%d\b' % (self._startport + 1), b':$HGPORT1'),
+            (br':%d\b' % (self._startport + 2), b':$HGPORT2'),
+            (br'(?m)^(saved backup bundle to .*\.hg)( \(glob\))?$',
+             br'\1 (glob)'),
             ]
 
         if os.name == 'nt':
             r.append(
-                (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
-                    c in '/\\' and r'[/\\]' or c.isdigit() and c or '\\' + c
-                    for c in self._testtmp), '$TESTTMP'))
+                (b''.join(c.isalpha() and b'[%s%s]' % (c.lower(), c.upper()) or
+                    c in b'/\\' and br'[/\\]' or c.isdigit() and c or b'\\' + c
+                    for c in self._testtmp), b'$TESTTMP'))
         else:
-            r.append((re.escape(self._testtmp), '$TESTTMP'))
+            r.append((re.escape(self._testtmp), b'$TESTTMP'))
 
         return r
 
@@ -837,15 +837,15 @@
 
     SKIPPED_PREFIX = 'skipped: '
     FAILED_PREFIX = 'hghave check failed: '
-    NEEDESCAPE = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
+    NEEDESCAPE = re.compile(br'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
 
-    ESCAPESUB = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
-    ESCAPEMAP = dict((chr(i), r'\x%02x' % i) for i in range(256))
-    ESCAPEMAP.update({'\\': '\\\\', '\r': r'\r'})
+    ESCAPESUB = re.compile(br'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
+    ESCAPEMAP = dict((bchr(i), br'\x%02x' % i) for i in range(256))
+    ESCAPEMAP.update({b'\\': b'\\\\', b'\r': br'\r'})
 
     @property
     def refpath(self):
-        return os.path.join(self._testdir, self.name)
+        return os.path.join(self._testdir, self.bname)
 
     def _run(self, env):
         f = open(self.path, 'rb')
@@ -855,13 +855,13 @@
         salt, script, after, expected = self._parsetest(lines)
 
         # Write out the generated script.
-        fname = '%s.sh' % self._testtmp
+        fname = b'%s.sh' % self._testtmp
         f = open(fname, 'wb')
         for l in script:
             f.write(l)
         f.close()
 
-        cmd = '%s "%s"' % (self._shell, fname)
+        cmd = b'%s "%s"' % (self._shell, fname)
         vlog("# Running", cmd)
 
         exitcode, output = self._runcommand(cmd, env)
@@ -878,9 +878,9 @@
 
     def _hghave(self, reqs):
         # TODO do something smarter when all other uses of hghave are gone.
-        tdir = self._testdir.replace('\\', '/')
-        proc = Popen4('%s -c "%s/hghave %s"' %
-                      (self._shell, tdir, ' '.join(reqs)),
+        tdir = self._testdir.replace(b'\\', b'/')
+        proc = Popen4(b'%s -c "%s/hghave %s"' %
+                      (self._shell, tdir, b' '.join(reqs)),
                       self._testtmp, 0, self._getenv())
         stdout, stderr = proc.communicate()
         ret = proc.wait()
@@ -896,12 +896,12 @@
         # We generate a shell script which outputs unique markers to line
         # up script results with our source. These markers include input
         # line number and the last return code.
-        salt = "SALT" + str(time.time())
+        salt = b"SALT%d" % time.time()
         def addsalt(line, inpython):
             if inpython:
-                script.append('%s %d 0\n' % (salt, line))
+                script.append(b'%s %d 0\n' % (salt, line))
             else:
-                script.append('echo %s %s $?\n' % (salt, line))
+                script.append(b'echo %s %d $?\n' % (salt, line))
 
         script = []
 
@@ -1020,8 +1020,8 @@
                 lout, lcmd = l.split(salt, 1)
 
             if lout:
-                if not lout.endswith('\n'):
-                    lout += ' (no-eol)\n'
+                if not lout.endswith(b'\n'):
+                    lout += b' (no-eol)\n'
 
                 # Find the expected output at the current position.
                 el = None
@@ -1040,12 +1040,12 @@
                         log('\ninfo, unknown linematch result: %r\n' % r)
                         r = False
                 if r:
-                    postout.append('  ' + el)
+                    postout.append(b'  ' + el)
                 else:
                     if self.NEEDESCAPE(lout):
-                        lout = TTest._stringescape('%s (esc)\n' %
-                                                   lout.rstrip('\n'))
-                    postout.append('  ' + lout) # Let diff deal with it.
+                        lout = TTest._stringescape(b'%s (esc)\n' %
+                                                   lout.rstrip(b'\n'))
+                    postout.append(b'  ' + lout) # Let diff deal with it.
                     if r != '': # If line failed.
                         warnonly = 3 # for sure not
                     elif warnonly == 1: # Is "not yet" and line is warn only.
@@ -1055,7 +1055,7 @@
                 # Add on last return code.
                 ret = int(lcmd.split()[1])
                 if ret != 0:
-                    postout.append('  [%s]\n' % ret)
+                    postout.append(b'  [%d]\n' % ret)
                 if pos in after:
                     # Merge in non-active test bits.
                     postout += after.pop(pos)
@@ -1074,8 +1074,8 @@
         try:
             # use \Z to ensure that the regex matches to the end of the string
             if os.name == 'nt':
-                return re.match(el + r'\r?\n\Z', l)
-            return re.match(el + r'\n\Z', l)
+                return re.match(el + br'\r?\n\Z', l)
+            return re.match(el + br'\n\Z', l)
         except re.error:
             # el is an invalid regex
             return False
@@ -1084,28 +1084,28 @@
     def globmatch(el, l):
         # The only supported special characters are * and ? plus / which also
         # matches \ on windows. Escaping of these characters is supported.
-        if el + '\n' == l:
+        if el + b'\n' == l:
             if os.altsep:
                 # matching on "/" is not needed for this line
                 for pat in checkcodeglobpats:
                     if pat.match(el):
                         return True
-                return '-glob'
+                return b'-glob'
             return True
         i, n = 0, len(el)
-        res = ''
+        res = b''
         while i < n:
-            c = el[i]
+            c = el[i:i + 1]
             i += 1
-            if c == '\\' and i < n and el[i] in '*?\\/':
+            if c == b'\\' and i < n and el[i:i + 1] in b'*?\\/':
                 res += el[i - 1:i + 1]
                 i += 1
-            elif c == '*':
-                res += '.*'
-            elif c == '?':
-                res += '.'
-            elif c == '/' and os.altsep:
-                res += '[/\\\\]'
+            elif c == b'*':
+                res += b'.*'
+            elif c == b'?':
+                res += b'.'
+            elif c == b'/' and os.altsep:
+                res += b'[/\\\\]'
             else:
                 res += re.escape(c)
         return TTest.rematch(res, l)
@@ -1115,19 +1115,21 @@
         if el == l: # perfect match (fast)
             return True
         if el:
-            if el.endswith(" (esc)\n"):
+            if el.endswith(b" (esc)\n"):
                 el = el[:-7].decode('string-escape') + '\n'
-            if el == l or os.name == 'nt' and el[:-1] + '\r\n' == l:
+                if sys.version_info[0] == 3:
+                    el.encode('utf-8')
+            if el == l or os.name == 'nt' and el[:-1] + b'\r\n' == l:
                 return True
-            if el.endswith(" (re)\n"):
+            if el.endswith(b" (re)\n"):
                 return TTest.rematch(el[:-6], l)
-            if el.endswith(" (glob)\n"):
+            if el.endswith(b" (glob)\n"):
                 # ignore '(glob)' added to l by 'replacements'
-                if l.endswith(" (glob)\n"):
-                    l = l[:-8] + "\n"
+                if l.endswith(b" (glob)\n"):
+                    l = l[:-8] + b"\n"
                 return TTest.globmatch(el[:-8], l)
-            if os.altsep and l.replace('\\', '/') == el:
-                return '+glob'
+            if os.altsep and l.replace(b'\\', b'/') == el:
+                return b'+glob'
         return False
 
     @staticmethod
@@ -1387,7 +1389,7 @@
             def get():
                 num_tests[0] += 1
                 if getattr(test, 'should_reload', False):
-                    return self._loadtest(test.name, num_tests[0])
+                    return self._loadtest(test.bname, num_tests[0])
                 return test
             if not os.path.exists(test.path):
                 result.addSkip(test, "Doesn't exist")
@@ -1603,19 +1605,19 @@
 
     # Programs required to run tests.
     REQUIREDTOOLS = [
-        os.path.basename(sys.executable),
-        'diff',
-        'grep',
-        'unzip',
-        'gunzip',
-        'bunzip2',
-        'sed',
+        os.path.basename(sys.executable).encode('utf-8'),
+        b'diff',
+        b'grep',
+        b'unzip',
+        b'gunzip',
+        b'bunzip2',
+        b'sed',
     ]
 
     # Maps file extensions to test class.
     TESTTYPES = [
-        ('.py', PythonTest),
-        ('.t', TTest),
+        (b'.py', PythonTest),
+        (b'.t', TTest),
     ]
 
     def __init__(self):
@@ -1639,6 +1641,7 @@
         try:
             parser = parser or getparser()
             options, args = parseargs(args, parser)
+            args = [a.encode('utf-8') for a in args]
             self.options = options
 
             self._checktools()
@@ -1652,7 +1655,7 @@
             random.shuffle(tests)
         else:
             # keywords for slow tests
-            slow = 'svn gendoc check-code-hg'.split()
+            slow = b'svn gendoc check-code-hg'.split()
             def sortkey(f):
                 # run largest tests first, as they tend to take the longest
                 try:
@@ -1667,7 +1670,8 @@
                 return val
             tests.sort(key=sortkey)
 
-        self._testdir = os.environ['TESTDIR'] = os.getcwd()
+        self._testdir = osenvironb[b'TESTDIR'] = getattr(
+            os, 'getcwdb', os.getcwd)()
 
         if 'PYTHONHASHSEED' not in os.environ:
             # use a random python hash seed all the time
@@ -1676,7 +1680,7 @@
 
         if self.options.tmpdir:
             self.options.keep_tmpdir = True
-            tmpdir = self.options.tmpdir
+            tmpdir = self.options.tmpdir.encode('utf-8')
             if os.path.exists(tmpdir):
                 # Meaning of tmpdir has changed since 1.3: we used to create
                 # HGTMP inside tmpdir; now HGTMP is tmpdir.  So fail if
@@ -1695,15 +1699,19 @@
             if os.name == 'nt':
                 # without this, we get the default temp dir location, but
                 # in all lowercase, which causes troubles with paths (issue3490)
-                d = os.getenv('TMP')
-            tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
-        self._hgtmp = os.environ['HGTMP'] = os.path.realpath(tmpdir)
+                d = osenvironb.get(b'TMP', None)
+            # FILE BUG: mkdtemp works only on unicode in Python 3
+            tmpdir = tempfile.mkdtemp('', 'hgtests.',
+                                      d and d.decode('utf-8')).encode('utf-8')
+
+        self._hgtmp = osenvironb[b'HGTMP'] = (
+            os.path.realpath(tmpdir))
 
         if self.options.with_hg:
             self._installdir = None
             self._bindir = os.path.dirname(os.path.realpath(
                                            self.options.with_hg))
-            self._tmpbindir = os.path.join(self._hgtmp, 'install', 'bin')
+            self._tmpbindir = os.path.join(self._hgtmp, b'install', b'bin')
             os.makedirs(self._tmpbindir)
 
             # This looks redundant with how Python initializes sys.path from
@@ -1713,25 +1721,30 @@
             # ... which means it's not really redundant at all.
             self._pythondir = self._bindir
         else:
-            self._installdir = os.path.join(self._hgtmp, "install")
-            self._bindir = os.environ["BINDIR"] = \
-                os.path.join(self._installdir, "bin")
+            self._installdir = os.path.join(self._hgtmp, b"install")
+            self._bindir = osenvironb[b"BINDIR"] = \
+                os.path.join(self._installdir, b"bin")
             self._tmpbindir = self._bindir
-            self._pythondir = os.path.join(self._installdir, "lib", "python")
+            self._pythondir = os.path.join(self._installdir, b"lib", b"python")
 
-        os.environ["BINDIR"] = self._bindir
+        osenvironb[b"BINDIR"] = self._bindir
         os.environ["PYTHON"] = PYTHON
 
-        runtestdir = os.path.abspath(os.path.dirname(__file__))
-        path = [self._bindir, runtestdir] + os.environ["PATH"].split(os.pathsep)
+        fileb = __file__.encode('utf-8')
+        runtestdir = os.path.abspath(os.path.dirname(fileb))
+        if sys.version_info[0] == 3:
+            sepb = os.pathsep.encode('utf-8')
+        else:
+            sepb = os.pathsep
+        path = [self._bindir, runtestdir] + osenvironb[b"PATH"].split(sepb)
         if os.path.islink(__file__):
             # test helper will likely be at the end of the symlink
-            realfile = os.path.realpath(__file__)
+            realfile = os.path.realpath(fileb)
             realdir = os.path.abspath(os.path.dirname(realfile))
             path.insert(2, realdir)
         if self._tmpbindir != self._bindir:
             path = [self._tmpbindir] + path
-        os.environ["PATH"] = os.pathsep.join(path)
+        osenvironb[b"PATH"] = sepb.join(path)
 
         # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
         # can run .../tests/run-tests.py test-foo where test-foo
@@ -1742,20 +1755,20 @@
         # it, in case external libraries are only available via current
         # PYTHONPATH.  (In particular, the Subversion bindings on OS X
         # are in /opt/subversion.)
-        oldpypath = os.environ.get(IMPL_PATH)
+        oldpypath = osenvironb.get(IMPL_PATH)
         if oldpypath:
             pypath.append(oldpypath)
-        os.environ[IMPL_PATH] = os.pathsep.join(pypath)
+        osenvironb[IMPL_PATH] = sepb.join(pypath)
 
         if self.options.pure:
             os.environ["HGTEST_RUN_TESTS_PURE"] = "--pure"
 
-        self._coveragefile = os.path.join(self._testdir, '.coverage')
+        self._coveragefile = os.path.join(self._testdir, b'.coverage')
 
         vlog("# Using TESTDIR", self._testdir)
         vlog("# Using HGTMP", self._hgtmp)
         vlog("# Using PATH", os.environ["PATH"])
-        vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
+        vlog("# Using", IMPL_PATH, osenvironb[IMPL_PATH])
 
         try:
             return self._runtests(tests) or 0
@@ -1774,13 +1787,13 @@
                 proc = Popen4('hg st --rev "%s" -man0 .' %
                               self.options.changed, None, 0)
                 stdout, stderr = proc.communicate()
-                args = stdout.strip('\0').split('\0')
+                args = stdout.strip(b'\0').split(b'\0')
             else:
-                args = os.listdir('.')
+                args = os.listdir(b'.')
 
         return [t for t in args
-                if os.path.basename(t).startswith('test-')
-                    and (t.endswith('.py') or t.endswith('.t'))]
+                if os.path.basename(t).startswith(b'test-')
+                    and (t.endswith(b'.py') or t.endswith(b'.t'))]
 
     def _runtests(self, tests):
         try:
@@ -1869,7 +1882,7 @@
                 break
 
         refpath = os.path.join(self._testdir, test)
-        tmpdir = os.path.join(self._hgtmp, 'child%d' % count)
+        tmpdir = os.path.join(self._hgtmp, b'child%d' % count)
 
         t = testcls(refpath, tmpdir,
                     keeptmpdir=self.options.keep_tmpdir,
@@ -1899,7 +1912,7 @@
     def _usecorrectpython(self):
         """Configure the environment to use the appropriate Python in tests."""
         # Tests must use the same interpreter as us or bad things will happen.
-        pyexename = sys.platform == 'win32' and 'python.exe' or 'python'
+        pyexename = sys.platform == 'win32' and b'python.exe' or b'python'
         if getattr(os, 'symlink', None):
             vlog("# Making python executable in test path a symlink to '%s'" %
                  sys.executable)
@@ -1967,7 +1980,7 @@
                ' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
                % {'exe': sys.executable, 'py3': py3, 'pure': pure,
                   'compiler': compiler,
-                  'base': os.path.join(self._hgtmp, "build"),
+                  'base': os.path.join(self._hgtmp, b"build"),
                   'prefix': self._installdir, 'libdir': self._pythondir,
                   'bindir': self._bindir,
                   'nohome': nohome, 'logfile': installerrs})