mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Jonas Rebmann <mail@schlaraffenlan.de>
To: Sascha Hauer <s.hauer@pengutronix.de>,
	 BAREBOX <barebox@lists.infradead.org>
Cc: Jonas Rebmann <mail@schlaraffenlan.de>
Subject: [PATCH v2] test: pytest: introduce pytest for network, test tftp notfound
Date: Tue, 08 Oct 2024 13:21:47 +0200	[thread overview]
Message-ID: <20241008-test_tftp-v2-1-509b8e861702@schlaraffenlan.de> (raw)

barebox already has a few pytest integration tests but none of them are
networking related.

This patch introduces a test that asserts on bringing up at least one
DHCP interface and, on any of these interfaces, a successful ICMP-ping
from the DUT to the test pytest-host as well as a basic "File not Found"
conversation between the DUT and the test pytest-host.

As the test requires a network connection to the barebox device, without
the "network" feature the test is marked xfail. For the case of a qemu
device we also assume DHCP to set serverip correctly. In any other case,
it is set via console to that IP of the pytest-host that has the route
to the barebox devices IP.

Signed-off-by: Jonas Rebmann <mail@schlaraffenlan.de>
---
Changes in v2:
- use random ephermal instead of hardcoded port for tftp test
- test any interface doing DHCP, expect one to test without issues
- fail on any tftp error code
- cleanup
- update commit message
- Link to v1: https://lore.barebox.org/barebox/20240605-test_tftp-v1-1-5752ea677b06@schlaraffenlan.de
---
 test/arm/virt@multi_v8_defconfig.yaml |   1 +
 test/openrisc/generic_defconfig.yaml  |   2 +
 test/py/test_network.py               | 106 ++++++++++++++++++++++++++++++++++
 3 files changed, 109 insertions(+)

diff --git a/test/arm/virt@multi_v8_defconfig.yaml b/test/arm/virt@multi_v8_defconfig.yaml
index d8f8ab5cbff35b5f9c414b0c7a66974a9a723794..42ce10328d7cc903010199364f5464d5146239e0 100644
--- a/test/arm/virt@multi_v8_defconfig.yaml
+++ b/test/arm/virt@multi_v8_defconfig.yaml
@@ -14,6 +14,7 @@ targets:
       BareboxTestStrategy: {}
     features:
       - virtio-mmio
+      - network
     runner:
       tuxmake_arch: arm64
 images:
diff --git a/test/openrisc/generic_defconfig.yaml b/test/openrisc/generic_defconfig.yaml
index 93ba9586c439d597c1a5e8deca44d1b99c902bed..56b70b8242f0e7f50f7ac732525a4857d8541744 100644
--- a/test/openrisc/generic_defconfig.yaml
+++ b/test/openrisc/generic_defconfig.yaml
@@ -12,6 +12,8 @@ targets:
         prompt: 'barebox@[^:]+:[^ ]+ '
         bootstring: 'commandline:'
       BareboxTestStrategy: {}
+    features:
+      - network
 images:
   barebox: !template "$LG_BUILDDIR/barebox"
 imports:
diff --git a/test/py/test_network.py b/test/py/test_network.py
new file mode 100644
index 0000000000000000000000000000000000000000..afaa259e2496740fabe61129e8e7d4190c5d4c50
--- /dev/null
+++ b/test/py/test_network.py
@@ -0,0 +1,106 @@
+import pytest
+
+from labgrid import driver,Environment
+import socket
+import threading
+import re
+import warnings
+
+# Setting the port to zero causes bind to choose a random ephermal port
+TFTP_TEST_PORT = 0
+
+def get_source_addr(destination_ip, destination_port):
+    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    udp_socket.connect((destination_ip, destination_port))
+    source_ip, _ = udp_socket.getsockname()
+    return source_ip
+
+def tftp_setup_socket(listen_addr, listen_port=0):
+    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    udp_socket.bind((listen_addr, listen_port))
+    udp_socket.settimeout(3)
+    return udp_socket
+
+
+def tftp_expect_notfound(filename, udp_socket):
+    uname = filename.encode("ascii")
+    messages = [
+        b"\000\001" + uname + b"\000octet\000timeout\0005\000blksize\000512\000tsize\0000\000",
+        b"\000\005\000\001File not found\000",
+    ]
+    data, addr = udp_socket.recvfrom(1024)
+    udp_socket.sendto(messages[1], addr)
+    udp_socket.close()
+    assert data == messages[0]
+
+def tftp_conversation(barebox, barebox_interface, guestaddr):
+
+    listen_addr = "127.0.0.1"
+    if not isinstance(barebox.console, driver.QEMUDriver):
+        # sending an arbitrary udp package to determine the IP towards DUT
+        listen_addr = get_source_addr(guestaddr, TFTP_TEST_PORT)
+        barebox.run_check(f"eth0.serverip={listen_addr}")
+
+    barebox.run_check("ping $eth0.serverip", timeout=2)
+
+    tftp_socket=tftp_setup_socket(listen_addr, TFTP_TEST_PORT)
+    tftp_port_used=tftp_socket.getsockname()[1]
+
+    tftp_thread = threading.Thread(
+        target=tftp_expect_notfound,
+        name="tftp_expect_notfound",
+        args=("a", tftp_socket),
+    )
+    tftp_thread.daemon = True
+    tftp_thread.start()
+
+    try:
+        stdout, _, returncode = barebox.run(f"tftp -P {tftp_port_used} a", timeout=3)
+        assert returncode != 0
+    finally:
+        # terminate a timed-out tftp
+        barebox.console.sendcontrol("c")
+        tftp_thread.join()
+        barebox.run_check("ifdown eth0")
+
+
+def test_barebox_network(barebox, env):
+    # on DUTs without network feature, this is expected to fail
+    # set xfail_strict=True to enforce specifying the network feature if available
+    if not 'network' in env.get_target_features():
+        pytest.xfail("network feature not specified")
+
+    stdout = barebox.run_check("ifup -a")
+
+    interfaces = []
+
+    for line in stdout:
+        # eth0: DHCP client bound to address 10.0.2.15
+        m = re.search('(eth[0-9]+): DHCP client bound to address (.*)', line)
+        if m is None:
+            continue
+        else:
+            interfaces.append((m.group(1), m.group(2)))
+
+    assert interfaces, "Network testing is only possible with at least one DHCP interface"
+
+    for iface in interfaces:
+        ifname = iface[0]
+        ifaddr = iface[1]
+
+        assert ifaddr != "0.0.0.0"
+        assert ifaddr == barebox.run_check(f"echo ${ifname}.ipaddr")[0]
+
+        # Attempt a conversation with the DUT, which needs to succeed only on one interface
+        success = False
+
+        try:
+            tftp_conversation(barebox, ifname, ifaddr)
+            success = True
+            break
+        except Exception as e:
+            warnings.warn(f"Could not connect to DUT on {ifname} ({ifaddr}): {e}")
+
+        if not success:
+            pytest.fail("Could not converse with DUT on any of the found DHCP interfaces!")
+

---
base-commit: 5a2866973d386ffe9b9f5c429a0cbcd58bcef69c
change-id: 20240605-test_tftp-9e12282d46a0

Best regards,
-- 
Jonas Rebmann <mail@schlaraffenlan.de>




             reply	other threads:[~2024-10-08 11:43 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-08 11:21 Jonas Rebmann [this message]
2024-10-09  5:46 ` Ahmad Fatoum
2024-10-14 12:58 ` Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241008-test_tftp-v2-1-509b8e861702@schlaraffenlan.de \
    --to=mail@schlaraffenlan.de \
    --cc=barebox@lists.infradead.org \
    --cc=s.hauer@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox