ping3
ping3 copied to clipboard
Unit tests failing
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.
What's the console output for the failed cases?
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#