336 """ |
336 """ |
337 |
337 |
338 # Status code reserved for skipped tests (used by hghave). |
338 # Status code reserved for skipped tests (used by hghave). |
339 SKIPPED_STATUS = 80 |
339 SKIPPED_STATUS = 80 |
340 |
340 |
341 def __init__(self, options, path, count, tmpdir, abort, keeptmpdir=False, |
341 def __init__(self, options, path, tmpdir, abort, keeptmpdir=False, |
342 debug=False, nodiff=False, diffviewer=None, |
342 debug=False, nodiff=False, diffviewer=None, |
343 interactive=False, timeout=defaults['timeout']): |
343 interactive=False, timeout=defaults['timeout'], |
|
344 startport=defaults['port']): |
344 """Create a test from parameters. |
345 """Create a test from parameters. |
345 |
346 |
346 options are parsed command line options that control test execution. |
347 options are parsed command line options that control test execution. |
347 |
348 |
348 path is the full path to the file defining the test. |
349 path is the full path to the file defining the test. |
349 |
|
350 count is an identifier used to denote this test instance. |
|
351 |
350 |
352 tmpdir is the main temporary directory to use for this test. |
351 tmpdir is the main temporary directory to use for this test. |
353 |
352 |
354 abort is a flag that turns to True if test execution should be aborted. |
353 abort is a flag that turns to True if test execution should be aborted. |
355 It is consulted periodically during the execution of tests. |
354 It is consulted periodically during the execution of tests. |
367 |
366 |
368 interactive controls whether the test will run interactively. |
367 interactive controls whether the test will run interactively. |
369 |
368 |
370 timeout controls the maximum run time of the test. It is ignored when |
369 timeout controls the maximum run time of the test. It is ignored when |
371 debug is True. |
370 debug is True. |
|
371 |
|
372 startport controls the starting port number to use for this test. Each |
|
373 test will reserve 3 port numbers for execution. It is the caller's |
|
374 responsibility to allocate a non-overlapping port range to Test |
|
375 instances. |
372 """ |
376 """ |
373 |
377 |
374 self.path = path |
378 self.path = path |
375 self.name = os.path.basename(path) |
379 self.name = os.path.basename(path) |
376 self._testdir = os.path.dirname(path) |
380 self._testdir = os.path.dirname(path) |
377 self.errpath = os.path.join(self._testdir, '%s.err' % self.name) |
381 self.errpath = os.path.join(self._testdir, '%s.err' % self.name) |
378 |
382 |
379 self._options = options |
383 self._options = options |
380 self._count = count |
|
381 self._threadtmp = tmpdir |
384 self._threadtmp = tmpdir |
382 self._abort = abort |
385 self._abort = abort |
383 self._keeptmpdir = keeptmpdir |
386 self._keeptmpdir = keeptmpdir |
384 self._debug = debug |
387 self._debug = debug |
385 self._nodiff = nodiff |
388 self._nodiff = nodiff |
386 self._diffviewer = diffviewer |
389 self._diffviewer = diffviewer |
387 self._interactive = interactive |
390 self._interactive = interactive |
388 self._timeout = timeout |
391 self._timeout = timeout |
|
392 self._startport = startport |
389 self._daemonpids = [] |
393 self._daemonpids = [] |
390 |
394 |
391 self._finished = None |
395 self._finished = None |
392 self._ret = None |
396 self._ret = None |
393 self._out = None |
397 self._out = None |
485 def runTest(self): |
489 def runTest(self): |
486 """Run this test instance. |
490 """Run this test instance. |
487 |
491 |
488 This will return a tuple describing the result of the test. |
492 This will return a tuple describing the result of the test. |
489 """ |
493 """ |
490 replacements, port = self._getreplacements() |
494 replacements = self._getreplacements() |
491 env = self._getenv(port) |
495 env = self._getenv() |
492 self._daemonpids.append(env['DAEMON_PIDS']) |
496 self._daemonpids.append(env['DAEMON_PIDS']) |
493 self._createhgrc(env['HGRCPATH']) |
497 self._createhgrc(env['HGRCPATH']) |
494 |
498 |
495 vlog('# Test', self.name) |
499 vlog('# Test', self.name) |
496 |
500 |
575 def _run(self, replacements, env): |
579 def _run(self, replacements, env): |
576 # This should be implemented in child classes to run tests. |
580 # This should be implemented in child classes to run tests. |
577 raise SkipTest('unknown test type') |
581 raise SkipTest('unknown test type') |
578 |
582 |
579 def _getreplacements(self): |
583 def _getreplacements(self): |
580 port = self._options.port + self._count * 3 |
|
581 r = [ |
584 r = [ |
582 (r':%s\b' % port, ':$HGPORT'), |
585 (r':%s\b' % self._startport, ':$HGPORT'), |
583 (r':%s\b' % (port + 1), ':$HGPORT1'), |
586 (r':%s\b' % (self._startport + 1), ':$HGPORT1'), |
584 (r':%s\b' % (port + 2), ':$HGPORT2'), |
587 (r':%s\b' % (self._startport + 2), ':$HGPORT2'), |
585 ] |
588 ] |
586 |
589 |
587 if os.name == 'nt': |
590 if os.name == 'nt': |
588 r.append( |
591 r.append( |
589 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or |
592 (''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or |
590 c in '/\\' and r'[/\\]' or c.isdigit() and c or '\\' + c |
593 c in '/\\' and r'[/\\]' or c.isdigit() and c or '\\' + c |
591 for c in self._testtmp), '$TESTTMP')) |
594 for c in self._testtmp), '$TESTTMP')) |
592 else: |
595 else: |
593 r.append((re.escape(self._testtmp), '$TESTTMP')) |
596 r.append((re.escape(self._testtmp), '$TESTTMP')) |
594 |
597 |
595 return r, port |
598 return r |
596 |
599 |
597 def _getenv(self, port): |
600 def _getenv(self): |
598 env = os.environ.copy() |
601 env = os.environ.copy() |
599 env['TESTTMP'] = self._testtmp |
602 env['TESTTMP'] = self._testtmp |
600 env['HOME'] = self._testtmp |
603 env['HOME'] = self._testtmp |
601 env["HGPORT"] = str(port) |
604 env["HGPORT"] = str(self._startport) |
602 env["HGPORT1"] = str(port + 1) |
605 env["HGPORT1"] = str(self._startport + 1) |
603 env["HGPORT2"] = str(port + 2) |
606 env["HGPORT2"] = str(self._startport + 2) |
604 env["HGRCPATH"] = os.path.join(self._threadtmp, '.hgrc') |
607 env["HGRCPATH"] = os.path.join(self._threadtmp, '.hgrc') |
605 env["DAEMON_PIDS"] = os.path.join(self._threadtmp, 'daemon.pids') |
608 env["DAEMON_PIDS"] = os.path.join(self._threadtmp, 'daemon.pids') |
606 env["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"' |
609 env["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"' |
607 env["HGMERGE"] = "internal:merge" |
610 env["HGMERGE"] = "internal:merge" |
608 env["HGUSER"] = "test" |
611 env["HGUSER"] = "test" |
1505 break |
1508 break |
1506 |
1509 |
1507 refpath = os.path.join(self.testdir, test) |
1510 refpath = os.path.join(self.testdir, test) |
1508 tmpdir = os.path.join(self.hgtmp, 'child%d' % count) |
1511 tmpdir = os.path.join(self.hgtmp, 'child%d' % count) |
1509 |
1512 |
1510 return testcls(self.options, refpath, count, tmpdir, self.abort, |
1513 return testcls(self.options, refpath, tmpdir, self.abort, |
1511 keeptmpdir=self.options.keep_tmpdir, |
1514 keeptmpdir=self.options.keep_tmpdir, |
1512 debug=self.options.debug, |
1515 debug=self.options.debug, |
1513 nodiff = self.options.nodiff, |
1516 nodiff = self.options.nodiff, |
1514 diffviewer=self.options.view, |
1517 diffviewer=self.options.view, |
1515 interactive=self.options.interactive, |
1518 interactive=self.options.interactive, |
1516 timeout=self.options.timeout) |
1519 timeout=self.options.timeout, |
|
1520 startport=self.options.port + count * 3) |
1517 |
1521 |
1518 def _cleanup(self): |
1522 def _cleanup(self): |
1519 """Clean up state from this test invocation.""" |
1523 """Clean up state from this test invocation.""" |
1520 |
1524 |
1521 if self.options.keep_tmpdir: |
1525 if self.options.keep_tmpdir: |