523 |
523 |
524 def tsttest(test, wd, options, replacements): |
524 def tsttest(test, wd, options, replacements): |
525 t = open(test) |
525 t = open(test) |
526 out = [] |
526 out = [] |
527 script = [] |
527 script = [] |
|
528 |
|
529 # We generate a shell script which outputs unique markers to line |
|
530 # up script results with our source. These markers include input |
|
531 # line number and the last return code |
528 salt = "SALT" + str(time.time()) |
532 salt = "SALT" + str(time.time()) |
529 |
533 def addsalt(line): |
|
534 script.append('echo %s %s $?\n' % (salt, line)) |
|
535 |
|
536 # After we run the shell script, we re-unify the script output |
|
537 # with non-active parts of the source, with synchronization by our |
|
538 # SALT line number markers. The after table contains the |
|
539 # non-active components, ordered by line number |
|
540 after = {} |
530 pos = prepos = -1 |
541 pos = prepos = -1 |
531 after = {} |
542 |
|
543 # Expected shellscript output |
532 expected = {} |
544 expected = {} |
|
545 |
|
546 # We keep track of whether or not we're in a Python block so we |
|
547 # can generate the surrounding doctest magic |
533 inpython = False |
548 inpython = False |
|
549 |
534 for n, l in enumerate(t): |
550 for n, l in enumerate(t): |
535 if not l.endswith('\n'): |
551 if not l.endswith('\n'): |
536 l += '\n' |
552 l += '\n' |
537 if l.startswith(' >>> '): |
553 if l.startswith(' >>> '): # python inlines |
538 if not inpython: |
554 if not inpython: |
539 # we've just entered a Python block, add the header |
555 # we've just entered a Python block, add the header |
540 inpython = True |
556 inpython = True |
541 script.append('echo %s %s $?\n' % (salt, n)) |
557 addsalt(n) |
542 script.append('%s -m heredoctest <<EOF\n' % PYTHON) |
558 script.append('%s -m heredoctest <<EOF\n' % PYTHON) |
543 prepos = pos |
559 prepos = pos |
544 pos = n |
560 pos = n |
545 after.setdefault(prepos, []).append(l) |
561 after.setdefault(prepos, []).append(l) |
546 script.append(l[2:]) |
562 script.append(l[2:]) |
549 script.append("EOF\n") |
565 script.append("EOF\n") |
550 inpython = False |
566 inpython = False |
551 after.setdefault(pos, []).append(l) |
567 after.setdefault(pos, []).append(l) |
552 prepos = pos |
568 prepos = pos |
553 pos = n |
569 pos = n |
554 script.append('echo %s %s $?\n' % (salt, n)) |
570 addsalt(n) |
555 script.append(l[4:]) |
571 script.append(l[4:]) |
556 elif l.startswith(' > '): # continuations |
572 elif l.startswith(' > '): # continuations |
557 after.setdefault(prepos, []).append(l) |
573 after.setdefault(prepos, []).append(l) |
558 script.append(l[4:]) |
574 script.append(l[4:]) |
559 elif l.startswith(' '): # results |
575 elif l.startswith(' '): # results |
568 script.append("EOF\n") |
584 script.append("EOF\n") |
569 inpython = False |
585 inpython = False |
570 # non-command/result - queue up for merged output |
586 # non-command/result - queue up for merged output |
571 after.setdefault(pos, []).append(l) |
587 after.setdefault(pos, []).append(l) |
572 |
588 |
|
589 t.close() |
|
590 |
573 if inpython: |
591 if inpython: |
574 script.append("EOF\n") |
592 script.append("EOF\n") |
575 |
593 addsalt(n + 1) |
576 t.close() |
|
577 |
|
578 script.append('echo %s %s $?\n' % (salt, n + 1)) |
|
579 |
594 |
580 fd, name = tempfile.mkstemp(suffix='hg-tst') |
595 fd, name = tempfile.mkstemp(suffix='hg-tst') |
581 |
|
582 try: |
596 try: |
583 for l in script: |
597 for l in script: |
584 os.write(fd, l) |
598 os.write(fd, l) |
585 os.close(fd) |
599 os.close(fd) |
586 |
600 |
619 res += '.' |
633 res += '.' |
620 else: |
634 else: |
621 res += re.escape(c) |
635 res += re.escape(c) |
622 return rematch(res, l) |
636 return rematch(res, l) |
623 |
637 |
|
638 # Merge the script output back into a unified test |
|
639 |
624 pos = -1 |
640 pos = -1 |
625 postout = [] |
641 postout = [] |
626 ret = 0 |
642 ret = 0 |
627 for n, l in enumerate(output): |
643 for n, l in enumerate(output): |
628 lout, lcmd = l, None |
644 lout, lcmd = l, None |
629 if salt in l: |
645 if salt in l: |
630 lout, lcmd = l.split(salt, 1) |
646 lout, lcmd = l.split(salt, 1) |
631 |
647 |
632 if lout: |
648 if lout: |
633 if lcmd: |
649 if lcmd: |
|
650 # output block had no trailing newline, clean up |
634 lout += ' (no-eol)\n' |
651 lout += ' (no-eol)\n' |
635 |
652 |
|
653 # find the expected output at the current position |
636 el = None |
654 el = None |
637 if pos in expected and expected[pos]: |
655 if pos in expected and expected[pos]: |
638 el = expected[pos].pop(0) |
656 el = expected[pos].pop(0) |
639 |
657 |
640 if el == lout: # perfect match (fast) |
658 if el == lout: # perfect match (fast) |
654 # add on last return code |
672 # add on last return code |
655 ret = int(lcmd.split()[1]) |
673 ret = int(lcmd.split()[1]) |
656 if ret != 0: |
674 if ret != 0: |
657 postout.append(" [%s]\n" % ret) |
675 postout.append(" [%s]\n" % ret) |
658 if pos in after: |
676 if pos in after: |
|
677 # merge in non-active test bits |
659 postout += after.pop(pos) |
678 postout += after.pop(pos) |
660 pos = int(lcmd.split()[0]) |
679 pos = int(lcmd.split()[0]) |
661 |
680 |
662 if pos in after: |
681 if pos in after: |
663 postout += after.pop(pos) |
682 postout += after.pop(pos) |