427 |
432 |
428 # Remove any previous output files. |
433 # Remove any previous output files. |
429 if os.path.exists(self._errpath): |
434 if os.path.exists(self._errpath): |
430 os.remove(self._errpath) |
435 os.remove(self._errpath) |
431 |
436 |
432 testtmp = os.path.join(self._threadtmp, os.path.basename(self._path)) |
437 replacements, port = self._getreplacements() |
433 os.mkdir(testtmp) |
438 env = self._getenv(port) |
434 replacements, port = self._getreplacements(testtmp) |
|
435 env = self._getenv(testtmp, port) |
|
436 self._daemonpids.append(env['DAEMON_PIDS']) |
439 self._daemonpids.append(env['DAEMON_PIDS']) |
437 self._createhgrc(env['HGRCPATH']) |
440 self._createhgrc(env['HGRCPATH']) |
438 |
441 |
439 vlog('# Test', self.name) |
442 vlog('# Test', self.name) |
440 |
443 |
441 starttime = time.time() |
444 starttime = time.time() |
442 try: |
445 try: |
443 ret, out = self._run(testtmp, replacements, env) |
446 ret, out = self._run(replacements, env) |
444 self._duration = time.time() - starttime |
447 self._duration = time.time() - starttime |
445 self._finished = True |
448 self._finished = True |
446 self._ret = ret |
449 self._ret = ret |
447 self._out = out |
450 self._out = out |
448 except KeyboardInterrupt: |
451 except KeyboardInterrupt: |
539 sys.stdout.flush() |
542 sys.stdout.flush() |
540 iolock.release() |
543 iolock.release() |
541 |
544 |
542 self._runner.times.append((self.name, self._duration)) |
545 self._runner.times.append((self.name, self._duration)) |
543 |
546 |
544 def _run(self, testtmp, replacements, env): |
547 def _run(self, replacements, env): |
545 # This should be implemented in child classes to run tests. |
548 # This should be implemented in child classes to run tests. |
546 return self._skip('unknown test type') |
549 return self._skip('unknown test type') |
547 |
550 |
548 def _getreplacements(self, testtmp): |
551 def _getreplacements(self): |
549 port = self._options.port + self._count * 3 |
552 port = self._options.port + self._count * 3 |
550 r = [ |
553 r = [ |
551 (r':%s\b' % port, ':$HGPORT'), |
554 (r':%s\b' % port, ':$HGPORT'), |
552 (r':%s\b' % (port + 1), ':$HGPORT1'), |
555 (r':%s\b' % (port + 1), ':$HGPORT1'), |
553 (r':%s\b' % (port + 2), ':$HGPORT2'), |
556 (r':%s\b' % (port + 2), ':$HGPORT2'), |
555 |
558 |
556 if os.name == 'nt': |
559 if os.name == 'nt': |
557 r.append( |
560 r.append( |
558 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or |
561 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or |
559 c in '/\\' and r'[/\\]' or c.isdigit() and c or '\\' + c |
562 c in '/\\' and r'[/\\]' or c.isdigit() and c or '\\' + c |
560 for c in testtmp), '$TESTTMP')) |
563 for c in self._testtmp), '$TESTTMP')) |
561 else: |
564 else: |
562 r.append((re.escape(testtmp), '$TESTTMP')) |
565 r.append((re.escape(self._testtmp), '$TESTTMP')) |
563 |
566 |
564 return r, port |
567 return r, port |
565 |
568 |
566 def _getenv(self, testtmp, port): |
569 def _getenv(self, port): |
567 env = os.environ.copy() |
570 env = os.environ.copy() |
568 env['TESTTMP'] = testtmp |
571 env['TESTTMP'] = self._testtmp |
569 env['HOME'] = testtmp |
572 env['HOME'] = self._testtmp |
570 env["HGPORT"] = str(port) |
573 env["HGPORT"] = str(port) |
571 env["HGPORT1"] = str(port + 1) |
574 env["HGPORT1"] = str(port + 1) |
572 env["HGPORT2"] = str(port + 2) |
575 env["HGPORT2"] = str(port + 2) |
573 env["HGRCPATH"] = os.path.join(self._threadtmp, '.hgrc') |
576 env["HGRCPATH"] = os.path.join(self._threadtmp, '.hgrc') |
574 env["DAEMON_PIDS"] = os.path.join(self._threadtmp, 'daemon.pids') |
577 env["DAEMON_PIDS"] = os.path.join(self._threadtmp, 'daemon.pids') |
664 |
667 |
665 return 'i', self.name, msg |
668 return 'i', self.name, msg |
666 |
669 |
667 class PythonTest(Test): |
670 class PythonTest(Test): |
668 """A Python-based test.""" |
671 """A Python-based test.""" |
669 def _run(self, testtmp, replacements, env): |
672 def _run(self, replacements, env): |
670 py3kswitch = self._options.py3k_warnings and ' -3' or '' |
673 py3kswitch = self._options.py3k_warnings and ' -3' or '' |
671 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, self._path) |
674 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, self._path) |
672 vlog("# Running", cmd) |
675 vlog("# Running", cmd) |
673 if os.name == 'nt': |
676 if os.name == 'nt': |
674 replacements.append((r'\r\n', '\n')) |
677 replacements.append((r'\r\n', '\n')) |
675 return run(cmd, testtmp, self._options, replacements, env, |
678 return run(cmd, self._testtmp, self._options, replacements, env, |
676 self._runner.abort) |
679 self._runner.abort) |
677 |
680 |
678 class TTest(Test): |
681 class TTest(Test): |
679 """A "t test" is a test backed by a .t file.""" |
682 """A "t test" is a test backed by a .t file.""" |
680 |
683 |
684 |
687 |
685 ESCAPESUB = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub |
688 ESCAPESUB = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub |
686 ESCAPEMAP = dict((chr(i), r'\x%02x' % i) for i in range(256)).update( |
689 ESCAPEMAP = dict((chr(i), r'\x%02x' % i) for i in range(256)).update( |
687 {'\\': '\\\\', '\r': r'\r'}) |
690 {'\\': '\\\\', '\r': r'\r'}) |
688 |
691 |
689 def _run(self, testtmp, replacements, env): |
692 def _run(self, replacements, env): |
690 f = open(self._path) |
693 f = open(self._path) |
691 lines = f.readlines() |
694 lines = f.readlines() |
692 f.close() |
695 f.close() |
693 |
696 |
694 salt, script, after, expected = self._parsetest(lines, testtmp) |
697 salt, script, after, expected = self._parsetest(lines) |
695 |
698 |
696 # Write out the generated script. |
699 # Write out the generated script. |
697 fname = '%s.sh' % testtmp |
700 fname = '%s.sh' % self._testtmp |
698 f = open(fname, 'w') |
701 f = open(fname, 'w') |
699 for l in script: |
702 for l in script: |
700 f.write(l) |
703 f.write(l) |
701 f.close() |
704 f.close() |
702 |
705 |
703 cmd = '%s "%s"' % (self._options.shell, fname) |
706 cmd = '%s "%s"' % (self._options.shell, fname) |
704 vlog("# Running", cmd) |
707 vlog("# Running", cmd) |
705 |
708 |
706 exitcode, output = run(cmd, testtmp, self._options, replacements, env, |
709 exitcode, output = run(cmd, self._testtmp, self._options, replacements, |
707 self._runner.abort) |
710 env, self._runner.abort) |
708 # Do not merge output if skipped. Return hghave message instead. |
711 # Do not merge output if skipped. Return hghave message instead. |
709 # Similarly, with --debug, output is None. |
712 # Similarly, with --debug, output is None. |
710 if exitcode == self.SKIPPED_STATUS or output is None: |
713 if exitcode == self.SKIPPED_STATUS or output is None: |
711 return exitcode, output |
714 return exitcode, output |
712 |
715 |
713 return self._processoutput(exitcode, output, salt, after, expected) |
716 return self._processoutput(exitcode, output, salt, after, expected) |
714 |
717 |
715 def _hghave(self, reqs, testtmp): |
718 def _hghave(self, reqs): |
716 # TODO do something smarter when all other uses of hghave are gone. |
719 # TODO do something smarter when all other uses of hghave are gone. |
717 tdir = self._testdir.replace('\\', '/') |
720 tdir = self._testdir.replace('\\', '/') |
718 proc = Popen4('%s -c "%s/hghave %s"' % |
721 proc = Popen4('%s -c "%s/hghave %s"' % |
719 (self._options.shell, tdir, ' '.join(reqs)), |
722 (self._options.shell, tdir, ' '.join(reqs)), |
720 testtmp, 0) |
723 self._testtmp, 0) |
721 stdout, stderr = proc.communicate() |
724 stdout, stderr = proc.communicate() |
722 ret = proc.wait() |
725 ret = proc.wait() |
723 if wifexited(ret): |
726 if wifexited(ret): |
724 ret = os.WEXITSTATUS(ret) |
727 ret = os.WEXITSTATUS(ret) |
725 if ret == 2: |
728 if ret == 2: |
726 print stdout |
729 print stdout |
727 sys.exit(1) |
730 sys.exit(1) |
728 |
731 |
729 return ret == 0 |
732 return ret == 0 |
730 |
733 |
731 def _parsetest(self, lines, testtmp): |
734 def _parsetest(self, lines): |
732 # We generate a shell script which outputs unique markers to line |
735 # We generate a shell script which outputs unique markers to line |
733 # up script results with our source. These markers include input |
736 # up script results with our source. These markers include input |
734 # line number and the last return code. |
737 # line number and the last return code. |
735 salt = "SALT" + str(time.time()) |
738 salt = "SALT" + str(time.time()) |
736 def addsalt(line, inpython): |
739 def addsalt(line, inpython): |
771 lsplit = l.split() |
774 lsplit = l.split() |
772 if len(lsplit) < 2 or lsplit[0] != '#if': |
775 if len(lsplit) < 2 or lsplit[0] != '#if': |
773 after.setdefault(pos, []).append(' !!! invalid #if\n') |
776 after.setdefault(pos, []).append(' !!! invalid #if\n') |
774 if skipping is not None: |
777 if skipping is not None: |
775 after.setdefault(pos, []).append(' !!! nested #if\n') |
778 after.setdefault(pos, []).append(' !!! nested #if\n') |
776 skipping = not self._hghave(lsplit[1:], testtmp) |
779 skipping = not self._hghave(lsplit[1:]) |
777 after.setdefault(pos, []).append(l) |
780 after.setdefault(pos, []).append(l) |
778 elif l.startswith('#else'): |
781 elif l.startswith('#else'): |
779 if skipping is None: |
782 if skipping is None: |
780 after.setdefault(pos, []).append(' !!! missing #if\n') |
783 after.setdefault(pos, []).append(' !!! missing #if\n') |
781 skipping = not skipping |
784 skipping = not skipping |