xref: /freebsd/tests/examples/test_examples.py (revision 864ea9abfb98493a157dc17be17c428080843bdd)
18161b823SAlexander V. Chernikovimport pytest
28161b823SAlexander V. Chernikovfrom atf_python.utils import BaseTest
38161b823SAlexander V. Chernikovfrom atf_python.sys.net.tools import ToolsHelper
48161b823SAlexander V. Chernikovfrom atf_python.sys.net.vnet import SingleVnetTestTemplate
58161b823SAlexander V. Chernikovfrom atf_python.sys.net.vnet import VnetTestTemplate
68161b823SAlexander V. Chernikovfrom atf_python.sys.net.vnet import VnetInstance
78161b823SAlexander V. Chernikov
88161b823SAlexander V. Chernikovimport errno
98161b823SAlexander V. Chernikovimport socket
108161b823SAlexander V. Chernikovimport subprocess
118161b823SAlexander V. Chernikovimport json
128161b823SAlexander V. Chernikov
138161b823SAlexander V. Chernikovfrom typing import List
148161b823SAlexander V. Chernikov
158161b823SAlexander V. Chernikov
168161b823SAlexander V. Chernikov# Test classes should be inherited
178161b823SAlexander V. Chernikov# from the BaseTest
188161b823SAlexander V. Chernikov
198161b823SAlexander V. Chernikov
208161b823SAlexander V. Chernikovclass TestExampleSimplest(BaseTest):
218161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
228161b823SAlexander V. Chernikov    def test_one(self):
238161b823SAlexander V. Chernikov        assert ToolsHelper.get_output("uname -s").strip() == "FreeBSD"
248161b823SAlexander V. Chernikov
258161b823SAlexander V. Chernikov
268161b823SAlexander V. Chernikovclass TestExampleSimple(BaseTest):
278161b823SAlexander V. Chernikov    # List of required kernel modules (kldstat -v)
288161b823SAlexander V. Chernikov    # that needs to be present for the tests to run
298161b823SAlexander V. Chernikov    REQUIRED_MODULES = ["null"]
308161b823SAlexander V. Chernikov
318161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
328161b823SAlexander V. Chernikov    def test_one(self):
338161b823SAlexander V. Chernikov        """Optional test description
348161b823SAlexander V. Chernikov        This and the following lines are not propagated
358161b823SAlexander V. Chernikov        to the ATF test description.
368161b823SAlexander V. Chernikov        """
378161b823SAlexander V. Chernikov        pass
388161b823SAlexander V. Chernikov
398161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
408161b823SAlexander V. Chernikov    # List of all requirements supported by an atf runner
418161b823SAlexander V. Chernikov    # See atf-test-case(4) for the detailed description
428161b823SAlexander V. Chernikov    @pytest.mark.require_user("root")
438161b823SAlexander V. Chernikov    @pytest.mark.require_arch(["amd64", "i386"])
448161b823SAlexander V. Chernikov    @pytest.mark.require_files(["/path/file1", "/path/file2"])
458161b823SAlexander V. Chernikov    @pytest.mark.require_machine(["amd64", "i386"])
468161b823SAlexander V. Chernikov    @pytest.mark.require_memory("200M")
478161b823SAlexander V. Chernikov    @pytest.mark.require_progs(["prog1", "prog2"])
488161b823SAlexander V. Chernikov    @pytest.mark.timeout(300)
498161b823SAlexander V. Chernikov    def test_two(self):
508161b823SAlexander V. Chernikov        pass
518161b823SAlexander V. Chernikov
528161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
53*864ea9abSAlexander V. Chernikov    def test_get_properties(self, request):
54*864ea9abSAlexander V. Chernikov        """Shows fetching of test src dir and ATF-set variables"""
55*864ea9abSAlexander V. Chernikov        print()
56*864ea9abSAlexander V. Chernikov        print("SRC_DIR={}".format(request.fspath.dirname))
57*864ea9abSAlexander V. Chernikov        print("ATF VARS:")
58*864ea9abSAlexander V. Chernikov        for k, v in self.atf_vars.items():
59*864ea9abSAlexander V. Chernikov            print("  {}: {}".format(k, v))
60*864ea9abSAlexander V. Chernikov        print()
61*864ea9abSAlexander V. Chernikov
62*864ea9abSAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
638161b823SAlexander V. Chernikov    @pytest.mark.require_user("unprivileged")
648161b823SAlexander V. Chernikov    def test_syscall_failure(self):
658161b823SAlexander V. Chernikov        s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
668161b823SAlexander V. Chernikov        with pytest.raises(OSError) as exc_info:
678161b823SAlexander V. Chernikov            s.bind(("::1", 42))
688161b823SAlexander V. Chernikov        assert exc_info.value.errno == errno.EACCES
698161b823SAlexander V. Chernikov
708161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
718161b823SAlexander V. Chernikov    @pytest.mark.parametrize(
728161b823SAlexander V. Chernikov        "family_tuple",
738161b823SAlexander V. Chernikov        [
748161b823SAlexander V. Chernikov            pytest.param([socket.AF_INET, None], id="AF_INET"),
758161b823SAlexander V. Chernikov            pytest.param([socket.AF_INET6, None], id="AF_INET6"),
768161b823SAlexander V. Chernikov            pytest.param([39, errno.EAFNOSUPPORT], id="FAMILY_39"),
778161b823SAlexander V. Chernikov        ],
788161b823SAlexander V. Chernikov    )
798161b823SAlexander V. Chernikov    def test_parametrize(self, family_tuple):
808161b823SAlexander V. Chernikov        family, error = family_tuple
818161b823SAlexander V. Chernikov        try:
828161b823SAlexander V. Chernikov            s = socket.socket(family, socket.SOCK_STREAM)
838161b823SAlexander V. Chernikov            s.close()
848161b823SAlexander V. Chernikov        except OSError as e:
858161b823SAlexander V. Chernikov            if error is None or error != e.errno:
868161b823SAlexander V. Chernikov                raise
878161b823SAlexander V. Chernikov
888161b823SAlexander V. Chernikov    # @pytest.mark.skip(reason="comment me to run the test")
898161b823SAlexander V. Chernikov    def test_with_cleanup(self):
908161b823SAlexander V. Chernikov        print("TEST BODY")
918161b823SAlexander V. Chernikov
928161b823SAlexander V. Chernikov    def cleanup_test_with_cleanup(self, test_id):
938161b823SAlexander V. Chernikov        print("CLEANUP HANDLER")
948161b823SAlexander V. Chernikov
958161b823SAlexander V. Chernikov
968161b823SAlexander V. Chernikovclass TestVnetSimple(SingleVnetTestTemplate):
978161b823SAlexander V. Chernikov    """
988161b823SAlexander V. Chernikov    SingleVnetTestTemplate creates a topology with a single
998161b823SAlexander V. Chernikov    vnet and a single epair between this vnet and the host system.
1008161b823SAlexander V. Chernikov    Additionally, lo0 interface is created inside the vnet.
1018161b823SAlexander V. Chernikov
1028161b823SAlexander V. Chernikov    Both vnets and interfaces are aliased as vnetX and ifY.
1038161b823SAlexander V. Chernikov    They can be accessed via maps:
1048161b823SAlexander V. Chernikov        vnet: VnetInstance = self.vnet_map["vnet1"]
1058161b823SAlexander V. Chernikov        iface: VnetInterface = vnet.iface_alias_map["if1"]
1068161b823SAlexander V. Chernikov
1078161b823SAlexander V. Chernikov    All prefixes from IPV4_PREFIXES and IPV6_PREFIXES are
1088161b823SAlexander V. Chernikov    assigned to the single epair interface inside the jail.
1098161b823SAlexander V. Chernikov
1108161b823SAlexander V. Chernikov    One can rely on the fact that there are no IPv6 prefixes
1118161b823SAlexander V. Chernikov    in the tentative state when the test method is called.
1128161b823SAlexander V. Chernikov    """
1138161b823SAlexander V. Chernikov
1148161b823SAlexander V. Chernikov    IPV6_PREFIXES: List[str] = ["2001:db8::1/64"]
1158161b823SAlexander V. Chernikov    IPV4_PREFIXES: List[str] = ["192.0.2.1/24"]
1168161b823SAlexander V. Chernikov
1178161b823SAlexander V. Chernikov    def setup_method(self, method):
1188161b823SAlexander V. Chernikov        """
1198161b823SAlexander V. Chernikov        Optional pre-setup for all of the tests inside the class
1208161b823SAlexander V. Chernikov        """
1218161b823SAlexander V. Chernikov        # Code to run before vnet setup
1228161b823SAlexander V. Chernikov        #
1238161b823SAlexander V. Chernikov        super().setup_method(method)
1248161b823SAlexander V. Chernikov        #
1258161b823SAlexander V. Chernikov        # Code to run after vnet setup
1268161b823SAlexander V. Chernikov        # Executed inside the vnet
1278161b823SAlexander V. Chernikov
1288161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
1298161b823SAlexander V. Chernikov    @pytest.mark.require_user("root")
1308161b823SAlexander V. Chernikov    def test_ping(self):
1318161b823SAlexander V. Chernikov        assert subprocess.run("ping -c1 192.0.2.1".split()).returncode == 0
1328161b823SAlexander V. Chernikov        assert subprocess.run("ping -c1 2001:db8::1".split()).returncode == 0
1338161b823SAlexander V. Chernikov
1348161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
1358161b823SAlexander V. Chernikov    def test_topology(self):
1368161b823SAlexander V. Chernikov        vnet = self.vnet_map["vnet1"]
1378161b823SAlexander V. Chernikov        iface = vnet.iface_alias_map["if1"]
1388161b823SAlexander V. Chernikov        print("Iface {} inside vnet {}".format(iface.name, vnet.name))
1398161b823SAlexander V. Chernikov
1408161b823SAlexander V. Chernikov
1418161b823SAlexander V. Chernikovclass TestVnetDual1(VnetTestTemplate):
1428161b823SAlexander V. Chernikov    """
1438161b823SAlexander V. Chernikov    VnetTestTemplate creates topology described in the self.TOPOLOGY
1448161b823SAlexander V. Chernikov
1458161b823SAlexander V. Chernikov    Each vnet (except vnet1) can have a handler function, named
1468161b823SAlexander V. Chernikov      vnetX_handler. This function will be run in a separate process
1478161b823SAlexander V. Chernikov      inside vnetX jail. The framework automatically creates a pipe
1488161b823SAlexander V. Chernikov      to allow communication between the main test and the vnet handler.
1498161b823SAlexander V. Chernikov
1508161b823SAlexander V. Chernikov    This topology contains 2 VNETs connected with 2 epairs:
1518161b823SAlexander V. Chernikov
1528161b823SAlexander V. Chernikov    [           VNET1          ]     [          VNET2           ]
1538161b823SAlexander V. Chernikov     if1(epair) 2001:db8:a::1/64 <-> 2001:db8:a::2/64 if1(epair)
1548161b823SAlexander V. Chernikov     if2(epair) 2001:db8:b::1/64 <-> 2001:db8:b::2/64 if2(epair)
1558161b823SAlexander V. Chernikov                 lo0                             lo0
1568161b823SAlexander V. Chernikov
1578161b823SAlexander V. Chernikov    """
1588161b823SAlexander V. Chernikov
1598161b823SAlexander V. Chernikov    TOPOLOGY = {
1608161b823SAlexander V. Chernikov        "vnet1": {"ifaces": ["if1", "if2"]},
1618161b823SAlexander V. Chernikov        "vnet2": {"ifaces": ["if1", "if2"]},
1628161b823SAlexander V. Chernikov        "if1": {"prefixes6": [("2001:db8:a::1/64", "2001:db8:a::2/64")]},
1638161b823SAlexander V. Chernikov        "if2": {"prefixes6": [("2001:db8:b::1/64", "2001:db8:b::2/64")]},
1648161b823SAlexander V. Chernikov    }
1658161b823SAlexander V. Chernikov
1668161b823SAlexander V. Chernikov    def _get_iface_stat(self, os_ifname: str):
1678161b823SAlexander V. Chernikov        out = ToolsHelper.get_output(
1688161b823SAlexander V. Chernikov            "{} -I {} --libxo json".format(ToolsHelper.NETSTAT_PATH, os_ifname)
1698161b823SAlexander V. Chernikov        )
1708161b823SAlexander V. Chernikov        js = json.loads(out)
1718161b823SAlexander V. Chernikov        return js["statistics"]["interface"][0]
1728161b823SAlexander V. Chernikov
1738161b823SAlexander V. Chernikov    def vnet2_handler(self, vnet: VnetInstance):
1748161b823SAlexander V. Chernikov        """
1758161b823SAlexander V. Chernikov        Test handler that runs in the vnet2 as a separate process.
1768161b823SAlexander V. Chernikov
1778161b823SAlexander V. Chernikov        This handler receives an interface name, fetches received/sent packets
1788161b823SAlexander V. Chernikov         and returns this data back to the parent process.
1798161b823SAlexander V. Chernikov        """
1808161b823SAlexander V. Chernikov        while True:
1818161b823SAlexander V. Chernikov            # receives 'ifX' with an infinite timeout
1828161b823SAlexander V. Chernikov            iface_alias = self.wait_object(vnet.pipe, None)
1838161b823SAlexander V. Chernikov            # Translates topology interface name to the actual OS-assigned name
1848161b823SAlexander V. Chernikov            os_ifname = vnet.iface_alias_map[iface_alias].name
1858161b823SAlexander V. Chernikov            self.send_object(vnet.pipe, self._get_iface_stat(os_ifname))
1868161b823SAlexander V. Chernikov
1878161b823SAlexander V. Chernikov    @pytest.mark.skip(reason="comment me to run the test")
1888161b823SAlexander V. Chernikov    @pytest.mark.require_user("root")
1898161b823SAlexander V. Chernikov    def test_ifstat(self):
1908161b823SAlexander V. Chernikov        """Checks that RX interface packets are properly accounted for"""
1918161b823SAlexander V. Chernikov        second_vnet = self.vnet_map["vnet2"]
1928161b823SAlexander V. Chernikov        pipe = second_vnet.pipe
1938161b823SAlexander V. Chernikov
1948161b823SAlexander V. Chernikov        # Ping neighbor IP on if1 and verify that the counter was incremented
1958161b823SAlexander V. Chernikov        self.send_object(pipe, "if1")
1968161b823SAlexander V. Chernikov        old_stat = self.wait_object(pipe)
1978161b823SAlexander V. Chernikov        assert subprocess.run("ping -c5 2001:db8:a::2".split()).returncode == 0
1988161b823SAlexander V. Chernikov        self.send_object(pipe, "if1")
1998161b823SAlexander V. Chernikov        new_stat = self.wait_object(pipe)
2008161b823SAlexander V. Chernikov        assert new_stat["received-packets"] - old_stat["received-packets"] >= 5
2018161b823SAlexander V. Chernikov
2028161b823SAlexander V. Chernikov        # Ping neighbor IP on if2 and verify that the counter was incremented
2038161b823SAlexander V. Chernikov        self.send_object(pipe, "if2")
2048161b823SAlexander V. Chernikov        old_stat = self.wait_object(pipe)
2058161b823SAlexander V. Chernikov        assert subprocess.run("ping -c5 2001:db8:b::2".split()).returncode == 0
2068161b823SAlexander V. Chernikov        self.send_object(pipe, "if2")
2078161b823SAlexander V. Chernikov        new_stat = self.wait_object(pipe)
2088161b823SAlexander V. Chernikov        assert new_stat["received-packets"] - old_stat["received-packets"] >= 5
209