| 628 | |
| 629 | |
| 630 | class BotProcess: |
| 631 | stdout_queue: queue.Queue |
| 632 | |
| 633 | def __init__(self, popen, addr) -> None: |
| 634 | self.popen = popen |
| 635 | |
| 636 | # The first thing the bot prints to stdout is an invite link. |
| 637 | self.qr = self.popen.stdout.readline() |
| 638 | self.addr = addr |
| 639 | |
| 640 | # we read stdout as quickly as we can in a thread and make |
| 641 | # the (unicode) lines available for readers through a queue. |
| 642 | self.stdout_queue = queue.Queue() |
| 643 | self.stdout_thread = t = threading.Thread(target=self._run_stdout_thread, name="bot-stdout-thread") |
| 644 | t.daemon = True |
| 645 | t.start() |
| 646 | |
| 647 | def _run_stdout_thread(self) -> None: |
| 648 | try: |
| 649 | while True: |
| 650 | line = self.popen.stdout.readline() |
| 651 | if not line: |
| 652 | break |
| 653 | line = line.strip() |
| 654 | self.stdout_queue.put(line) |
| 655 | print("bot-stdout: ", line) |
| 656 | finally: |
| 657 | self.stdout_queue.put(None) |
| 658 | |
| 659 | def kill(self) -> None: |
| 660 | self.popen.kill() |
| 661 | |
| 662 | def wait(self, timeout=None) -> None: |
| 663 | self.popen.wait(timeout=timeout) |
| 664 | |
| 665 | def fnmatch_lines(self, pattern_lines): |
| 666 | patterns = [x.strip() for x in Source(pattern_lines.rstrip()).lines if x.strip()] |
| 667 | for next_pattern in patterns: |
| 668 | print("+++FNMATCH:", next_pattern) |
| 669 | ignored = [] |
| 670 | while True: |
| 671 | line = self.stdout_queue.get() |
| 672 | if line is None: |
| 673 | if ignored: |
| 674 | print("BOT stdout terminated after these lines") |
| 675 | for line in ignored: |
| 676 | print(line) |
| 677 | raise IOError("BOT stdout-thread terminated") |
| 678 | if fnmatch.fnmatch(line, next_pattern): |
| 679 | print("+++MATCHED:", line) |
| 680 | break |
| 681 | else: |
| 682 | print("+++IGN:", line) |
| 683 | ignored.append(line) |
| 684 | |
| 685 | |
| 686 | @pytest.fixture() |
no outgoing calls