Repository: bitcoin/bitcoin
File: test/functional/test_framework/test_node.py (Python)

Code before:
        Block until we see a particular debug log message fragment or until we exceed the timeout.
        Return:
            the number of log lines we encountered when matching
        """
        time_end = time.time() + timeout * self.timeout_factor
        prev_size = self.debug_log_size(mode="rb")  # Must use same mode that is used to read() below

        yield

        while True:
            found = True
            with open(self.debug_log_path, "rb") as dl:
                dl.seek(prev_size)
                log = dl.read()

            for expected_msg in expected_msgs:
                if expected_msg not in log:
                    found = False

            if found:
                return

            if time.time() >= time_end:
                print_log = " - " + "\n - ".join(log.decode("utf8", errors="replace").splitlines())
                break

            # No sleep here because we want to detect the message fragment as fast as
            # possible.

        self._raise_assertion_error(
            'Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(
                str(expected_msgs), print_log))

    @contextlib.contextmanager
    def wait_for_new_peer(self, timeout=5):
        """
        Wait until the node is connected to at least one new peer. We detect this
        by watching for an increased highest peer id, using the `getpeerinfo` RPC call.
        Note that the simpler approach of only accounting for the number of peers
        suffers from race conditions, as disconnects from unrelated previous peers
        could happen anytime in-between.
        """
        def get_highest_peer_id():
            peer_info = self.getpeerinfo()
            return peer_info[-1]["id"] if peer_info else -1

        initial_peer_id = get_highest_peer_id()
        yield
        self.wait_until(lambda: get_highest_peer_id() > initial_peer_id, timeout=timeout)

    @contextlib.contextmanager

Review comment (suggestion):
Wouldn't this return if the same expected message appears multiple times in the log? I think we should collect the indexes into a set instead. Not sure that will keep the expected order (which we likely should), I haven't checked or thought about extensively.


Code after:

    @contextlib.contextmanager
    def busy_wait_for_debug_log(self, expected_msgs, timeout=60):
        """
        Block until we see a particular debug log message fragment or until we exceed the timeout.
        """
        time_end = time.time() + timeout * self.timeout_factor
        prev_size = self.debug_log_size(mode="rb")  # Must use same mode that is used to read() below
        remaining_expected = list(expected_msgs)

        yield

        while True:
            with open(self.debug_log_path, "rb") as dl:
                dl.seek(prev_size)
                log = dl.read()

            while remaining_expected and remaining_expected[-1] in log:
                remaining_expected.pop()
            if not remaining_expected:
                return

            if time.time() >= time_end:
                print_log = " - " + "\n - ".join(log.decode("utf8", errors="replace").splitlines())
                break

            # No sleep here because we want to detect the message fragment as fast as
            # possible.

        remaining_expected = [e for e in remaining_expected if e not in log]
        self._raise_assertion_error(f'Expected message(s) {remaining_expected!s} '
                                    f'not found in log:\n\n{print_log}\n\n')

    @contextlib.contextmanager
    def wait_for_new_peer(self, timeout=5):
        """
        Wait until the node is connected to at least one new peer. We detect this
        by watching for an increased highest peer id, using the `getpeerinfo` RPC call.
        Note that the simpler approach of only accounting for the number of peers
        suffers from race conditions, as disconnects from unrelated previous peers
        could happen anytime in-between.
        """
        def get_highest_peer_id():
            peer_info = self.getpeerinfo()
            return peer_info[-1]["id"] if peer_info else -1

        initial_peer_id = get_highest_peer_id()
        yield
        self.wait_until(lambda: get_highest_peer_id() > initial_peer_id, timeout=timeout)

    @contextlib.contextmanager