664 Test output needs to be normalized so it can be compared to expected |
664 Test output needs to be normalized so it can be compared to expected |
665 output. This function defines how some of that normalization will |
665 output. This function defines how some of that normalization will |
666 occur. |
666 occur. |
667 """ |
667 """ |
668 r = [ |
668 r = [ |
669 (r':%s\b' % self._startport, ':$HGPORT'), |
669 (br':%d\b' % self._startport, b':$HGPORT'), |
670 (r':%s\b' % (self._startport + 1), ':$HGPORT1'), |
670 (br':%d\b' % (self._startport + 1), b':$HGPORT1'), |
671 (r':%s\b' % (self._startport + 2), ':$HGPORT2'), |
671 (br':%d\b' % (self._startport + 2), b':$HGPORT2'), |
672 (r'(?m)^(saved backup bundle to .*\.hg)( \(glob\))?$', |
672 (br'(?m)^(saved backup bundle to .*\.hg)( \(glob\))?$', |
673 r'\1 (glob)'), |
673 br'\1 (glob)'), |
674 ] |
674 ] |
675 |
675 |
676 if os.name == 'nt': |
676 if os.name == 'nt': |
677 r.append( |
677 r.append( |
678 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or |
678 (b''.join(c.isalpha() and b'[%s%s]' % (c.lower(), c.upper()) or |
679 c in '/\\' and r'[/\\]' or c.isdigit() and c or '\\' + c |
679 c in b'/\\' and br'[/\\]' or c.isdigit() and c or b'\\' + c |
680 for c in self._testtmp), '$TESTTMP')) |
680 for c in self._testtmp), b'$TESTTMP')) |
681 else: |
681 else: |
682 r.append((re.escape(self._testtmp), '$TESTTMP')) |
682 r.append((re.escape(self._testtmp), b'$TESTTMP')) |
683 |
683 |
684 return r |
684 return r |
685 |
685 |
686 def _getenv(self): |
686 def _getenv(self): |
687 """Obtain environment variables to use during test execution.""" |
687 """Obtain environment variables to use during test execution.""" |
835 class TTest(Test): |
835 class TTest(Test): |
836 """A "t test" is a test backed by a .t file.""" |
836 """A "t test" is a test backed by a .t file.""" |
837 |
837 |
838 SKIPPED_PREFIX = 'skipped: ' |
838 SKIPPED_PREFIX = 'skipped: ' |
839 FAILED_PREFIX = 'hghave check failed: ' |
839 FAILED_PREFIX = 'hghave check failed: ' |
840 NEEDESCAPE = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search |
840 NEEDESCAPE = re.compile(br'[\x00-\x08\x0b-\x1f\x7f-\xff]').search |
841 |
841 |
842 ESCAPESUB = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub |
842 ESCAPESUB = re.compile(br'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub |
843 ESCAPEMAP = dict((chr(i), r'\x%02x' % i) for i in range(256)) |
843 ESCAPEMAP = dict((bchr(i), br'\x%02x' % i) for i in range(256)) |
844 ESCAPEMAP.update({'\\': '\\\\', '\r': r'\r'}) |
844 ESCAPEMAP.update({b'\\': b'\\\\', b'\r': br'\r'}) |
845 |
845 |
846 @property |
846 @property |
847 def refpath(self): |
847 def refpath(self): |
848 return os.path.join(self._testdir, self.name) |
848 return os.path.join(self._testdir, self.bname) |
849 |
849 |
850 def _run(self, env): |
850 def _run(self, env): |
851 f = open(self.path, 'rb') |
851 f = open(self.path, 'rb') |
852 lines = f.readlines() |
852 lines = f.readlines() |
853 f.close() |
853 f.close() |
854 |
854 |
855 salt, script, after, expected = self._parsetest(lines) |
855 salt, script, after, expected = self._parsetest(lines) |
856 |
856 |
857 # Write out the generated script. |
857 # Write out the generated script. |
858 fname = '%s.sh' % self._testtmp |
858 fname = b'%s.sh' % self._testtmp |
859 f = open(fname, 'wb') |
859 f = open(fname, 'wb') |
860 for l in script: |
860 for l in script: |
861 f.write(l) |
861 f.write(l) |
862 f.close() |
862 f.close() |
863 |
863 |
864 cmd = '%s "%s"' % (self._shell, fname) |
864 cmd = b'%s "%s"' % (self._shell, fname) |
865 vlog("# Running", cmd) |
865 vlog("# Running", cmd) |
866 |
866 |
867 exitcode, output = self._runcommand(cmd, env) |
867 exitcode, output = self._runcommand(cmd, env) |
868 |
868 |
869 if self._aborted: |
869 if self._aborted: |
876 |
876 |
877 return self._processoutput(exitcode, output, salt, after, expected) |
877 return self._processoutput(exitcode, output, salt, after, expected) |
878 |
878 |
879 def _hghave(self, reqs): |
879 def _hghave(self, reqs): |
880 # TODO do something smarter when all other uses of hghave are gone. |
880 # TODO do something smarter when all other uses of hghave are gone. |
881 tdir = self._testdir.replace('\\', '/') |
881 tdir = self._testdir.replace(b'\\', b'/') |
882 proc = Popen4('%s -c "%s/hghave %s"' % |
882 proc = Popen4(b'%s -c "%s/hghave %s"' % |
883 (self._shell, tdir, ' '.join(reqs)), |
883 (self._shell, tdir, b' '.join(reqs)), |
884 self._testtmp, 0, self._getenv()) |
884 self._testtmp, 0, self._getenv()) |
885 stdout, stderr = proc.communicate() |
885 stdout, stderr = proc.communicate() |
886 ret = proc.wait() |
886 ret = proc.wait() |
887 if wifexited(ret): |
887 if wifexited(ret): |
888 ret = os.WEXITSTATUS(ret) |
888 ret = os.WEXITSTATUS(ret) |
1038 r = '' # Warn only this line. |
1038 r = '' # Warn only this line. |
1039 else: |
1039 else: |
1040 log('\ninfo, unknown linematch result: %r\n' % r) |
1040 log('\ninfo, unknown linematch result: %r\n' % r) |
1041 r = False |
1041 r = False |
1042 if r: |
1042 if r: |
1043 postout.append(' ' + el) |
1043 postout.append(b' ' + el) |
1044 else: |
1044 else: |
1045 if self.NEEDESCAPE(lout): |
1045 if self.NEEDESCAPE(lout): |
1046 lout = TTest._stringescape('%s (esc)\n' % |
1046 lout = TTest._stringescape(b'%s (esc)\n' % |
1047 lout.rstrip('\n')) |
1047 lout.rstrip(b'\n')) |
1048 postout.append(' ' + lout) # Let diff deal with it. |
1048 postout.append(b' ' + lout) # Let diff deal with it. |
1049 if r != '': # If line failed. |
1049 if r != '': # If line failed. |
1050 warnonly = 3 # for sure not |
1050 warnonly = 3 # for sure not |
1051 elif warnonly == 1: # Is "not yet" and line is warn only. |
1051 elif warnonly == 1: # Is "not yet" and line is warn only. |
1052 warnonly = 2 # Yes do warn. |
1052 warnonly = 2 # Yes do warn. |
1053 |
1053 |
1054 if lcmd: |
1054 if lcmd: |
1055 # Add on last return code. |
1055 # Add on last return code. |
1056 ret = int(lcmd.split()[1]) |
1056 ret = int(lcmd.split()[1]) |
1057 if ret != 0: |
1057 if ret != 0: |
1058 postout.append(' [%s]\n' % ret) |
1058 postout.append(b' [%d]\n' % ret) |
1059 if pos in after: |
1059 if pos in after: |
1060 # Merge in non-active test bits. |
1060 # Merge in non-active test bits. |
1061 postout += after.pop(pos) |
1061 postout += after.pop(pos) |
1062 pos = int(lcmd.split()[0]) |
1062 pos = int(lcmd.split()[0]) |
1063 |
1063 |
1072 @staticmethod |
1072 @staticmethod |
1073 def rematch(el, l): |
1073 def rematch(el, l): |
1074 try: |
1074 try: |
1075 # use \Z to ensure that the regex matches to the end of the string |
1075 # use \Z to ensure that the regex matches to the end of the string |
1076 if os.name == 'nt': |
1076 if os.name == 'nt': |
1077 return re.match(el + r'\r?\n\Z', l) |
1077 return re.match(el + br'\r?\n\Z', l) |
1078 return re.match(el + r'\n\Z', l) |
1078 return re.match(el + br'\n\Z', l) |
1079 except re.error: |
1079 except re.error: |
1080 # el is an invalid regex |
1080 # el is an invalid regex |
1081 return False |
1081 return False |
1082 |
1082 |
1083 @staticmethod |
1083 @staticmethod |
1084 def globmatch(el, l): |
1084 def globmatch(el, l): |
1085 # The only supported special characters are * and ? plus / which also |
1085 # The only supported special characters are * and ? plus / which also |
1086 # matches \ on windows. Escaping of these characters is supported. |
1086 # matches \ on windows. Escaping of these characters is supported. |
1087 if el + '\n' == l: |
1087 if el + b'\n' == l: |
1088 if os.altsep: |
1088 if os.altsep: |
1089 # matching on "/" is not needed for this line |
1089 # matching on "/" is not needed for this line |
1090 for pat in checkcodeglobpats: |
1090 for pat in checkcodeglobpats: |
1091 if pat.match(el): |
1091 if pat.match(el): |
1092 return True |
1092 return True |
1093 return '-glob' |
1093 return b'-glob' |
1094 return True |
1094 return True |
1095 i, n = 0, len(el) |
1095 i, n = 0, len(el) |
1096 res = '' |
1096 res = b'' |
1097 while i < n: |
1097 while i < n: |
1098 c = el[i] |
1098 c = el[i:i + 1] |
1099 i += 1 |
1099 i += 1 |
1100 if c == '\\' and i < n and el[i] in '*?\\/': |
1100 if c == b'\\' and i < n and el[i:i + 1] in b'*?\\/': |
1101 res += el[i - 1:i + 1] |
1101 res += el[i - 1:i + 1] |
1102 i += 1 |
1102 i += 1 |
1103 elif c == '*': |
1103 elif c == b'*': |
1104 res += '.*' |
1104 res += b'.*' |
1105 elif c == '?': |
1105 elif c == b'?': |
1106 res += '.' |
1106 res += b'.' |
1107 elif c == '/' and os.altsep: |
1107 elif c == b'/' and os.altsep: |
1108 res += '[/\\\\]' |
1108 res += b'[/\\\\]' |
1109 else: |
1109 else: |
1110 res += re.escape(c) |
1110 res += re.escape(c) |
1111 return TTest.rematch(res, l) |
1111 return TTest.rematch(res, l) |
1112 |
1112 |
1113 @staticmethod |
1113 @staticmethod |
1114 def linematch(el, l): |
1114 def linematch(el, l): |
1115 if el == l: # perfect match (fast) |
1115 if el == l: # perfect match (fast) |
1116 return True |
1116 return True |
1117 if el: |
1117 if el: |
1118 if el.endswith(" (esc)\n"): |
1118 if el.endswith(b" (esc)\n"): |
1119 el = el[:-7].decode('string-escape') + '\n' |
1119 el = el[:-7].decode('string-escape') + '\n' |
1120 if el == l or os.name == 'nt' and el[:-1] + '\r\n' == l: |
1120 if sys.version_info[0] == 3: |
|
1121 el.encode('utf-8') |
|
1122 if el == l or os.name == 'nt' and el[:-1] + b'\r\n' == l: |
1121 return True |
1123 return True |
1122 if el.endswith(" (re)\n"): |
1124 if el.endswith(b" (re)\n"): |
1123 return TTest.rematch(el[:-6], l) |
1125 return TTest.rematch(el[:-6], l) |
1124 if el.endswith(" (glob)\n"): |
1126 if el.endswith(b" (glob)\n"): |
1125 # ignore '(glob)' added to l by 'replacements' |
1127 # ignore '(glob)' added to l by 'replacements' |
1126 if l.endswith(" (glob)\n"): |
1128 if l.endswith(b" (glob)\n"): |
1127 l = l[:-8] + "\n" |
1129 l = l[:-8] + b"\n" |
1128 return TTest.globmatch(el[:-8], l) |
1130 return TTest.globmatch(el[:-8], l) |
1129 if os.altsep and l.replace('\\', '/') == el: |
1131 if os.altsep and l.replace(b'\\', b'/') == el: |
1130 return '+glob' |
1132 return b'+glob' |
1131 return False |
1133 return False |
1132 |
1134 |
1133 @staticmethod |
1135 @staticmethod |
1134 def parsehghaveoutput(lines): |
1136 def parsehghaveoutput(lines): |
1135 '''Parse hghave log lines. |
1137 '''Parse hghave log lines. |
1665 if kw in f: |
1668 if kw in f: |
1666 val *= 10 |
1669 val *= 10 |
1667 return val |
1670 return val |
1668 tests.sort(key=sortkey) |
1671 tests.sort(key=sortkey) |
1669 |
1672 |
1670 self._testdir = os.environ['TESTDIR'] = os.getcwd() |
1673 self._testdir = osenvironb[b'TESTDIR'] = getattr( |
|
1674 os, 'getcwdb', os.getcwd)() |
1671 |
1675 |
1672 if 'PYTHONHASHSEED' not in os.environ: |
1676 if 'PYTHONHASHSEED' not in os.environ: |
1673 # use a random python hash seed all the time |
1677 # use a random python hash seed all the time |
1674 # we do the randomness ourself to know what seed is used |
1678 # we do the randomness ourself to know what seed is used |
1675 os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32)) |
1679 os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32)) |
1676 |
1680 |
1677 if self.options.tmpdir: |
1681 if self.options.tmpdir: |
1678 self.options.keep_tmpdir = True |
1682 self.options.keep_tmpdir = True |
1679 tmpdir = self.options.tmpdir |
1683 tmpdir = self.options.tmpdir.encode('utf-8') |
1680 if os.path.exists(tmpdir): |
1684 if os.path.exists(tmpdir): |
1681 # Meaning of tmpdir has changed since 1.3: we used to create |
1685 # Meaning of tmpdir has changed since 1.3: we used to create |
1682 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if |
1686 # HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if |
1683 # tmpdir already exists. |
1687 # tmpdir already exists. |
1684 print("error: temp dir %r already exists" % tmpdir) |
1688 print("error: temp dir %r already exists" % tmpdir) |
1693 else: |
1697 else: |
1694 d = None |
1698 d = None |
1695 if os.name == 'nt': |
1699 if os.name == 'nt': |
1696 # without this, we get the default temp dir location, but |
1700 # without this, we get the default temp dir location, but |
1697 # in all lowercase, which causes troubles with paths (issue3490) |
1701 # in all lowercase, which causes troubles with paths (issue3490) |
1698 d = os.getenv('TMP') |
1702 d = osenvironb.get(b'TMP', None) |
1699 tmpdir = tempfile.mkdtemp('', 'hgtests.', d) |
1703 # FILE BUG: mkdtemp works only on unicode in Python 3 |
1700 self._hgtmp = os.environ['HGTMP'] = os.path.realpath(tmpdir) |
1704 tmpdir = tempfile.mkdtemp('', 'hgtests.', |
|
1705 d and d.decode('utf-8')).encode('utf-8') |
|
1706 |
|
1707 self._hgtmp = osenvironb[b'HGTMP'] = ( |
|
1708 os.path.realpath(tmpdir)) |
1701 |
1709 |
1702 if self.options.with_hg: |
1710 if self.options.with_hg: |
1703 self._installdir = None |
1711 self._installdir = None |
1704 self._bindir = os.path.dirname(os.path.realpath( |
1712 self._bindir = os.path.dirname(os.path.realpath( |
1705 self.options.with_hg)) |
1713 self.options.with_hg)) |
1706 self._tmpbindir = os.path.join(self._hgtmp, 'install', 'bin') |
1714 self._tmpbindir = os.path.join(self._hgtmp, b'install', b'bin') |
1707 os.makedirs(self._tmpbindir) |
1715 os.makedirs(self._tmpbindir) |
1708 |
1716 |
1709 # This looks redundant with how Python initializes sys.path from |
1717 # This looks redundant with how Python initializes sys.path from |
1710 # the location of the script being executed. Needed because the |
1718 # the location of the script being executed. Needed because the |
1711 # "hg" specified by --with-hg is not the only Python script |
1719 # "hg" specified by --with-hg is not the only Python script |
1712 # executed in the test suite that needs to import 'mercurial' |
1720 # executed in the test suite that needs to import 'mercurial' |
1713 # ... which means it's not really redundant at all. |
1721 # ... which means it's not really redundant at all. |
1714 self._pythondir = self._bindir |
1722 self._pythondir = self._bindir |
1715 else: |
1723 else: |
1716 self._installdir = os.path.join(self._hgtmp, "install") |
1724 self._installdir = os.path.join(self._hgtmp, b"install") |
1717 self._bindir = os.environ["BINDIR"] = \ |
1725 self._bindir = osenvironb[b"BINDIR"] = \ |
1718 os.path.join(self._installdir, "bin") |
1726 os.path.join(self._installdir, b"bin") |
1719 self._tmpbindir = self._bindir |
1727 self._tmpbindir = self._bindir |
1720 self._pythondir = os.path.join(self._installdir, "lib", "python") |
1728 self._pythondir = os.path.join(self._installdir, b"lib", b"python") |
1721 |
1729 |
1722 os.environ["BINDIR"] = self._bindir |
1730 osenvironb[b"BINDIR"] = self._bindir |
1723 os.environ["PYTHON"] = PYTHON |
1731 os.environ["PYTHON"] = PYTHON |
1724 |
1732 |
1725 runtestdir = os.path.abspath(os.path.dirname(__file__)) |
1733 fileb = __file__.encode('utf-8') |
1726 path = [self._bindir, runtestdir] + os.environ["PATH"].split(os.pathsep) |
1734 runtestdir = os.path.abspath(os.path.dirname(fileb)) |
|
1735 if sys.version_info[0] == 3: |
|
1736 sepb = os.pathsep.encode('utf-8') |
|
1737 else: |
|
1738 sepb = os.pathsep |
|
1739 path = [self._bindir, runtestdir] + osenvironb[b"PATH"].split(sepb) |
1727 if os.path.islink(__file__): |
1740 if os.path.islink(__file__): |
1728 # test helper will likely be at the end of the symlink |
1741 # test helper will likely be at the end of the symlink |
1729 realfile = os.path.realpath(__file__) |
1742 realfile = os.path.realpath(fileb) |
1730 realdir = os.path.abspath(os.path.dirname(realfile)) |
1743 realdir = os.path.abspath(os.path.dirname(realfile)) |
1731 path.insert(2, realdir) |
1744 path.insert(2, realdir) |
1732 if self._tmpbindir != self._bindir: |
1745 if self._tmpbindir != self._bindir: |
1733 path = [self._tmpbindir] + path |
1746 path = [self._tmpbindir] + path |
1734 os.environ["PATH"] = os.pathsep.join(path) |
1747 osenvironb[b"PATH"] = sepb.join(path) |
1735 |
1748 |
1736 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions |
1749 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions |
1737 # can run .../tests/run-tests.py test-foo where test-foo |
1750 # can run .../tests/run-tests.py test-foo where test-foo |
1738 # adds an extension to HGRC. Also include run-test.py directory to |
1751 # adds an extension to HGRC. Also include run-test.py directory to |
1739 # import modules like heredoctest. |
1752 # import modules like heredoctest. |
1740 pypath = [self._pythondir, self._testdir, runtestdir] |
1753 pypath = [self._pythondir, self._testdir, runtestdir] |
1741 # We have to augment PYTHONPATH, rather than simply replacing |
1754 # We have to augment PYTHONPATH, rather than simply replacing |
1742 # it, in case external libraries are only available via current |
1755 # it, in case external libraries are only available via current |
1743 # PYTHONPATH. (In particular, the Subversion bindings on OS X |
1756 # PYTHONPATH. (In particular, the Subversion bindings on OS X |
1744 # are in /opt/subversion.) |
1757 # are in /opt/subversion.) |
1745 oldpypath = os.environ.get(IMPL_PATH) |
1758 oldpypath = osenvironb.get(IMPL_PATH) |
1746 if oldpypath: |
1759 if oldpypath: |
1747 pypath.append(oldpypath) |
1760 pypath.append(oldpypath) |
1748 os.environ[IMPL_PATH] = os.pathsep.join(pypath) |
1761 osenvironb[IMPL_PATH] = sepb.join(pypath) |
1749 |
1762 |
1750 if self.options.pure: |
1763 if self.options.pure: |
1751 os.environ["HGTEST_RUN_TESTS_PURE"] = "--pure" |
1764 os.environ["HGTEST_RUN_TESTS_PURE"] = "--pure" |
1752 |
1765 |
1753 self._coveragefile = os.path.join(self._testdir, '.coverage') |
1766 self._coveragefile = os.path.join(self._testdir, b'.coverage') |
1754 |
1767 |
1755 vlog("# Using TESTDIR", self._testdir) |
1768 vlog("# Using TESTDIR", self._testdir) |
1756 vlog("# Using HGTMP", self._hgtmp) |
1769 vlog("# Using HGTMP", self._hgtmp) |
1757 vlog("# Using PATH", os.environ["PATH"]) |
1770 vlog("# Using PATH", os.environ["PATH"]) |
1758 vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH]) |
1771 vlog("# Using", IMPL_PATH, osenvironb[IMPL_PATH]) |
1759 |
1772 |
1760 try: |
1773 try: |
1761 return self._runtests(tests) or 0 |
1774 return self._runtests(tests) or 0 |
1762 finally: |
1775 finally: |
1763 time.sleep(.1) |
1776 time.sleep(.1) |
1965 ' install --force --prefix="%(prefix)s"' |
1978 ' install --force --prefix="%(prefix)s"' |
1966 ' --install-lib="%(libdir)s"' |
1979 ' --install-lib="%(libdir)s"' |
1967 ' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1' |
1980 ' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1' |
1968 % {'exe': sys.executable, 'py3': py3, 'pure': pure, |
1981 % {'exe': sys.executable, 'py3': py3, 'pure': pure, |
1969 'compiler': compiler, |
1982 'compiler': compiler, |
1970 'base': os.path.join(self._hgtmp, "build"), |
1983 'base': os.path.join(self._hgtmp, b"build"), |
1971 'prefix': self._installdir, 'libdir': self._pythondir, |
1984 'prefix': self._installdir, 'libdir': self._pythondir, |
1972 'bindir': self._bindir, |
1985 'bindir': self._bindir, |
1973 'nohome': nohome, 'logfile': installerrs}) |
1986 'nohome': nohome, 'logfile': installerrs}) |
1974 |
1987 |
1975 # setuptools requires install directories to exist. |
1988 # setuptools requires install directories to exist. |