ping3 icon indicating copy to clipboard operation
ping3 copied to clipboard

Unit tests failing

Open cpina opened this issue 1 year ago • 2 comments

I tried to run the unit tests (it helps when doing the Debian package) and there are a few unit tests that fail. I had a look and some have "easy" fixes. I don't have them in a handy repo (because they need root) but you can see two fixes here: https://gist.github.com/cpina/ff5feff11803b5e74cc073386fadecd9

It would be great to fix all of them.

cpina avatar Oct 24 '23 21:10 cpina

What's the console output for the failed cases?

kyan001 avatar Oct 25 '23 01:10 kyan001

root@pinux:~/git/ping3# pytest
==================================== test session starts =====================================
platform linux -- Python 3.11.2, pytest-7.2.1, pluggy-1.0.0+repack
PySide2 5.15.8 -- Qt runtime 5.15.8 -- Qt compiled 5.15.8
rootdir: /root/git/ping3
plugins: anyio-3.6.2, cov-4.0.0, qt-4.2.0+repack, xvfb-2.0.0
collected 39 items                                                                           

tests/test_command_line.py ............                                                [ 30%]
tests/test_ping3.py ....FF....F.....F......F...                                        [100%]

========================================== FAILURES ==========================================
________________________________ test_ping3.test_ping_normal _________________________________

self = <test_ping3.test_ping3 testMethod=test_ping_normal>

    def test_ping_normal(self):
        delay = ping3.ping(DEST_DOMAIN)
>       self.assertIsInstance(delay, float)
E       AssertionError: False is not an instance of <class 'float'>

tests/test_ping3.py:29: AssertionError
__________________________________ test_ping3.test_ping_seq __________________________________

self = <test_ping3.test_ping3 testMethod=test_ping_seq>

    def test_ping_seq(self):
        delay = ping3.ping(DEST_DOMAIN, seq=199)
>       self.assertIsInstance(delay, float)
E       AssertionError: False is not an instance of <class 'float'>

tests/test_ping3.py:68: AssertionError
___________________________ test_ping3.test_ping_timeout_exception ___________________________

self = <test_ping3.test_ping3 testMethod=test_ping_timeout_exception>

    def test_ping_timeout_exception(self):
        with patch("ping3.EXCEPTIONS", True):
            with self.assertRaises(ping3.errors.Timeout):
>               ping3.ping(DEST_DOMAIN, timeout=0.0001)

tests/test_ping3.py:43: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
ping3/__init__.py:83: in wrapper
    func_return = func(*args, **kwargs)
ping3/__init__.py:318: in ping
    _raise(err)
ping3/__init__.py:65: in _raise
    raise err
ping3/__init__.py:311: in ping
    delay = receive_one_ping(sock=sock, icmp_id=icmp_id, seq=seq, timeout=timeout)  # in seconds
ping3/__init__.py:83: in wrapper
    func_return = func(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    @_func_logger
    def receive_one_ping(sock: socket, icmp_id: int, seq: int, timeout: int) -> float:
        """Receives the ping from the socket.
    
        IP Header (bits): version (8), type of service (8), length (16), id (16), flags (16), time to live (8), protocol (8), checksum (16), source ip (32), destination ip (32).
        ICMP Packet (bytes): IP Header (20), ICMP Header (8), ICMP Payload (*).
        Ping Wikipedia: https://en.wikipedia.org/wiki/Ping_(networking_utility)
        ToS (Type of Service) in IP header for ICMP is 0. Protocol in IP header for ICMP is 1.
    
        Args:
            sock: The same socket used for send the ping.
            icmp_id: ICMP packet id. Sent packet id should be identical with received packet id.
            seq: ICMP packet sequence. Sent packet sequence should be identical with received packet sequence.
            timeout: Timeout in seconds.
    
        Returns:
            The delay in seconds or None on timeout.
    
        Raises:
            TimeToLiveExpired: If the Time-To-Live in IP Header is not large enough for destination.
            TimeExceeded: If time exceeded but Time-To-Live does not expired.
            DestinationHostUnreachable: If the destination host is unreachable.
            DestinationUnreachable: If the destination is unreachable.
        """
        has_ip_header = (os.name != 'posix') or (platform.system() == 'Darwin') or (sock.type == socket.SOCK_RAW)  # No IP Header when unprivileged on Linux.
        if has_ip_header:
            ip_header_slice = slice(0, struct.calcsize(IP_HEADER_FORMAT))  # [0:20]
            icmp_header_slice = slice(ip_header_slice.stop, ip_header_slice.stop + struct.calcsize(ICMP_HEADER_FORMAT))  # [20:28]
        else:
            _debug("Unprivileged on Linux")
            icmp_header_slice = slice(0, struct.calcsize(ICMP_HEADER_FORMAT))  # [0:8]
        timeout_time = time.time() + timeout  # Exactly time when timeout.
        _debug("Timeout time: {} ({})".format(time.ctime(timeout_time), timeout_time))
        while True:
            timeout_left = timeout_time - time.time()  # How many seconds left until timeout.
            timeout_left = timeout_left if timeout_left > 0 else 0  # Timeout must be non-negative
            _debug("Timeout left: {:.2f}s".format(timeout_left))
            selected = select.select([sock, ], [], [], timeout_left)  # Wait until sock is ready to read or time is out.
            if selected[0] == []:  # Timeout
                raise errors.Timeout(timeout=timeout)
            time_recv = time.time()
            _debug("Received time: {} ({}))".format(time.ctime(time_recv), time_recv))
            recv_data, addr = sock.recvfrom(1500)  # Single packet size limit is 65535 bytes, but usually the network packet limit is 1500 bytes.
            if has_ip_header:
                ip_header_raw = recv_data[ip_header_slice]
                ip_header = read_ip_header(ip_header_raw)
                _debug("Received IP header:", ip_header)
            else:
                ip_header = None
            icmp_header_raw, icmp_payload_raw = recv_data[icmp_header_slice], recv_data[icmp_header_slice.stop:]
            icmp_header = read_icmp_header(icmp_header_raw)
            _debug("Received ICMP header:", icmp_header)
            _debug("Received ICMP payload:", icmp_payload_raw)
            if not has_ip_header:  # When unprivileged on Linux, ICMP ID is rewrited by kernel.
                icmp_id = sock.getsockname()[1]  # According to https://stackoverflow.com/a/14023878/4528364
            if icmp_header['type'] == IcmpType.TIME_EXCEEDED:  # TIME_EXCEEDED has no icmp_id and icmp_seq. Usually they are 0.
                if icmp_header['code'] == IcmpTimeExceededCode.TTL_EXPIRED:  # Windows raw socket cannot get TTL_EXPIRED. See https://stackoverflow.com/questions/43239862/socket-sock-raw-ipproto-icmp-cant-read-ttl-response.
                    raise errors.TimeToLiveExpired(ip_header=ip_header, icmp_header=icmp_header)  # Some router does not report TTL expired and then timeout shows.
                raise errors.TimeExceeded()
            if icmp_header['type'] == IcmpType.DESTINATION_UNREACHABLE:  # DESTINATION_UNREACHABLE has no icmp_id and icmp_seq. Usually they are 0.
                if icmp_header['code'] == IcmpDestinationUnreachableCode.DESTINATION_HOST_UNREACHABLE:
                    raise errors.DestinationHostUnreachable(ip_header=ip_header, icmp_header=icmp_header)
>               raise errors.DestinationUnreachable(ip_header=ip_header, icmp_header=icmp_header)
E               ping3.errors.DestinationUnreachable: Destination unreachable. (Host='127.0.0.1')

ping3/__init__.py:241: DestinationUnreachable
___________________________ test_ping3.test_verbose_ping_interval ____________________________

self = <test_ping3.test_ping3 testMethod=test_verbose_ping_interval>

    def test_verbose_ping_interval(self):
        with patch("sys.stdout", new=io.StringIO()) as fake_out:
            delay = ping3.ping(DEST_DOMAIN)
>           self.assertTrue(0 < delay < 0.75)  # If interval does not work, the total delay should be < 3s (4 * 0.75s)
E           AssertionError: False is not true

tests/test_ping3.py:178: AssertionError
______________________________ test_ping3.test_verbose_ping_ttl ______________________________

self = <test_ping3.test_ping3 testMethod=test_verbose_ping_ttl>

    @unittest.skipIf(sys.platform.startswith("win"), "Linux and macOS Only")
    def test_verbose_ping_ttl(self):
        with patch("sys.stdout", new=io.StringIO()) as fake_out:
            ping3.verbose_ping(DEST_DOMAIN, ttl=1)
>           self.assertNotRegex(fake_out.getvalue(), r".*[0-9]+ms.*")
E           AssertionError: Regex matched: "ping 'example.com' ... 139ms" matches '.*[0-9]+ms.*' in "ping 'example.com' ... Error\nping 'example.com' ... 139ms\nping 'example.com' ... Error\nping 'example.com' ... Error\n"

tests/test_ping3.py:162: AssertionError
================================== short test summary info ===================================
FAILED tests/test_ping3.py::test_ping3::test_ping_normal - AssertionError: False is not an instance of <class 'float'>
FAILED tests/test_ping3.py::test_ping3::test_ping_seq - AssertionError: False is not an instance of <class 'float'>
FAILED tests/test_ping3.py::test_ping3::test_ping_timeout_exception - ping3.errors.DestinationUnreachable: Destination unreachable. (Host='127.0.0.1')
FAILED tests/test_ping3.py::test_ping3::test_verbose_ping_interval - AssertionError: False is not true
FAILED tests/test_ping3.py::test_ping3::test_verbose_ping_ttl - AssertionError: Regex matched: "ping 'example.com' ... 139ms" matches '.*[0-9]+ms.*' in "...
=============================== 5 failed, 34 passed in 50.20s ================================
root@pinux:~/git/ping3# 

cpina avatar Oct 25 '23 06:10 cpina