tests/run-tests.py
changeset 21454 046587aa1c8a
parent 21453 aaf52b78327e
child 21455 0f0bace82149
equal deleted inserted replaced
21453:aaf52b78327e 21454:046587aa1c8a
   360         self._ret = None
   360         self._ret = None
   361         self._out = None
   361         self._out = None
   362         self._duration = None
   362         self._duration = None
   363         self._result = None
   363         self._result = None
   364         self._skipped = None
   364         self._skipped = None
       
   365         self._testtmp = None
   365 
   366 
   366         # If we're not in --debug mode and reference output file exists,
   367         # If we're not in --debug mode and reference output file exists,
   367         # check test output against it.
   368         # check test output against it.
   368         if runner.options.debug:
   369         if runner.options.debug:
   369             self._refout = None # to match "out is None"
   370             self._refout = None # to match "out is None"
   391         self._out = None
   392         self._out = None
   392         self._duration = None
   393         self._duration = None
   393         self._result = None
   394         self._result = None
   394         self._skipped = None
   395         self._skipped = None
   395 
   396 
       
   397         self._testtmp = os.path.join(self._threadtmp,
       
   398                                      os.path.basename(self._path))
       
   399         os.mkdir(self._testtmp)
       
   400 
   396     def run(self):
   401     def run(self):
   397         """Run this test instance.
   402         """Run this test instance.
   398 
   403 
   399         This will return a tuple describing the result of the test.
   404         This will return a tuple describing the result of the test.
   400         """
   405         """
   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:
   452             raise
   455             raise
   453         except Exception, e:
   456         except Exception, e:
   454             return self.fail('Exception during execution: %s' % e, 255)
   457             return self.fail('Exception during execution: %s' % e, 255)
   455 
   458 
   456         killdaemons(env['DAEMON_PIDS'])
   459         killdaemons(env['DAEMON_PIDS'])
   457 
       
   458         if not options.keep_tmpdir:
       
   459             shutil.rmtree(testtmp)
       
   460 
   460 
   461         def describe(ret):
   461         def describe(ret):
   462             if ret < 0:
   462             if ret < 0:
   463                 return 'killed by signal: %d' % -ret
   463                 return 'killed by signal: %d' % -ret
   464             return 'returned error code %d' % ret
   464             return 'returned error code %d' % ret
   527 
   527 
   528         return self._result
   528         return self._result
   529 
   529 
   530     def tearDown(self):
   530     def tearDown(self):
   531         """Tasks to perform after run()."""
   531         """Tasks to perform after run()."""
       
   532         if not self._options.keep_tmpdir:
       
   533             shutil.rmtree(self._testtmp)
       
   534 
   532         vlog("# Ret was:", self._ret)
   535         vlog("# Ret was:", self._ret)
   533 
   536 
   534         # Don't print progress in unittest mode because that is handled
   537         # Don't print progress in unittest mode because that is handled
   535         # by TestResult.
   538         # by TestResult.
   536         if not self._options.verbose and not self._unittest:
   539         if not self._options.verbose and not self._unittest:
   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