aboutsummaryrefslogtreecommitdiff
path: root/tests/examples/test_examples.py
blob: fe9ae0a72cf2a70fee6e10e5fda52412d0616347 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
import pytest
from atf_python.utils import BaseTest
from atf_python.sys.net.tools import ToolsHelper
from atf_python.sys.net.vnet import SingleVnetTestTemplate
from atf_python.sys.net.vnet import VnetTestTemplate
from atf_python.sys.net.vnet import VnetInstance

import errno
import socket
import subprocess
import json

from typing import List


# Test classes should be inherited
# from the BaseTest


class TestExampleSimplest(BaseTest):
    @pytest.mark.skip(reason="comment me to run the test")
    def test_one(self):
        assert ToolsHelper.get_output("uname -s").strip() == "FreeBSD"


class TestExampleSimple(BaseTest):
    # List of required kernel modules (kldstat -v)
    # that needs to be present for the tests to run
    REQUIRED_MODULES = ["null"]

    @pytest.mark.skip(reason="comment me to run the test")
    def test_one(self):
        """Optional test description
        This and the following lines are not propagated
        to the ATF test description.
        """
        pass

    @pytest.mark.skip(reason="comment me to run the test")
    # List of all requirements supported by an atf runner
    # See atf-test-case(4) for the detailed description
    @pytest.mark.require_user("root")
    @pytest.mark.require_arch(["amd64", "i386"])
    @pytest.mark.require_files(["/path/file1", "/path/file2"])
    @pytest.mark.require_machine(["amd64", "i386"])
    @pytest.mark.require_memory("200M")
    @pytest.mark.require_progs(["prog1", "prog2"])
    @pytest.mark.timeout(300)
    def test_two(self):
        pass

    @pytest.mark.skip(reason="comment me to run the test")
    def test_get_properties(self, request):
        """Shows fetching of test src dir and ATF-set variables"""
        print()
        print("SRC_DIR={}".format(request.fspath.dirname))
        print("ATF VARS:")
        for k, v in self.atf_vars.items():
            print("  {}: {}".format(k, v))
        print()

    @pytest.mark.skip(reason="comment me to run the test")
    @pytest.mark.require_user("unprivileged")
    def test_syscall_failure(self):
        s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
        with pytest.raises(OSError) as exc_info:
            s.bind(("::1", 42))
        assert exc_info.value.errno == errno.EACCES

    @pytest.mark.skip(reason="comment me to run the test")
    @pytest.mark.parametrize(
        "family_tuple",
        [
            pytest.param([socket.AF_INET, None], id="AF_INET"),
            pytest.param([socket.AF_INET6, None], id="AF_INET6"),
            pytest.param([39, errno.EAFNOSUPPORT], id="FAMILY_39"),
        ],
    )
    def test_parametrize(self, family_tuple):
        family, error = family_tuple
        try:
            s = socket.socket(family, socket.SOCK_STREAM)
            s.close()
        except OSError as e:
            if error is None or error != e.errno:
                raise

    # @pytest.mark.skip(reason="comment me to run the test")
    def test_with_cleanup(self):
        print("TEST BODY")

    def cleanup_test_with_cleanup(self, test_id):
        print("CLEANUP HANDLER")


class TestVnetSimple(SingleVnetTestTemplate):
    """
    SingleVnetTestTemplate creates a topology with a single
    vnet and a single epair between this vnet and the host system.
    Additionally, lo0 interface is created inside the vnet.

    Both vnets and interfaces are aliased as vnetX and ifY.
    They can be accessed via maps:
        vnet: VnetInstance = self.vnet_map["vnet1"]
        iface: VnetInterface = vnet.iface_alias_map["if1"]

    All prefixes from IPV4_PREFIXES and IPV6_PREFIXES are
    assigned to the single epair interface inside the jail.

    One can rely on the fact that there are no IPv6 prefixes
    in the tentative state when the test method is called.
    """

    IPV6_PREFIXES: List[str] = ["2001:db8::1/64"]
    IPV4_PREFIXES: List[str] = ["192.0.2.1/24"]

    def setup_method(self, method):
        """
        Optional pre-setup for all of the tests inside the class
        """
        # Code to run before vnet setup
        #
        super().setup_method(method)
        #
        # Code to run after vnet setup
        # Executed inside the vnet

    @pytest.mark.skip(reason="comment me to run the test")
    @pytest.mark.require_user("root")
    def test_ping(self):
        assert subprocess.run("ping -c1 192.0.2.1".split()).returncode == 0
        assert subprocess.run("ping -c1 2001:db8::1".split()).returncode == 0

    @pytest.mark.skip(reason="comment me to run the test")
    def test_topology(self):
        vnet = self.vnet_map["vnet1"]
        iface = vnet.iface_alias_map["if1"]
        print("Iface {} inside vnet {}".format(iface.name, vnet.name))


class TestVnetDual1(VnetTestTemplate):
    """
    VnetTestTemplate creates topology described in the self.TOPOLOGY

    Each vnet (except vnet1) can have a handler function, named
      vnetX_handler. This function will be run in a separate process
      inside vnetX jail. The framework automatically creates a pipe
      to allow communication between the main test and the vnet handler.

    This topology contains 2 VNETs connected with 2 epairs:

    [           VNET1          ]     [          VNET2           ]
     if1(epair) 2001:db8:a::1/64 <-> 2001:db8:a::2/64 if1(epair)
     if2(epair) 2001:db8:b::1/64 <-> 2001:db8:b::2/64 if2(epair)
                 lo0                             lo0

    """

    TOPOLOGY = {
        "vnet1": {"ifaces": ["if1", "if2"]},
        "vnet2": {"ifaces": ["if1", "if2"]},
        "if1": {"prefixes6": [("2001:db8:a::1/64", "2001:db8:a::2/64")]},
        "if2": {"prefixes6": [("2001:db8:b::1/64", "2001:db8:b::2/64")]},
    }

    def _get_iface_stat(self, os_ifname: str):
        out = ToolsHelper.get_output(
            "{} -I {} --libxo json".format(ToolsHelper.NETSTAT_PATH, os_ifname)
        )
        js = json.loads(out)
        return js["statistics"]["interface"][0]

    def vnet2_handler(self, vnet: VnetInstance):
        """
        Test handler that runs in the vnet2 as a separate process.

        This handler receives an interface name, fetches received/sent packets
         and returns this data back to the parent process.
        """
        while True:
            # receives 'ifX' with an infinite timeout
            iface_alias = self.wait_object(vnet.pipe, None)
            # Translates topology interface name to the actual OS-assigned name
            os_ifname = vnet.iface_alias_map[iface_alias].name
            self.send_object(vnet.pipe, self._get_iface_stat(os_ifname))

    @pytest.mark.skip(reason="comment me to run the test")
    @pytest.mark.require_user("root")
    def test_ifstat(self):
        """Checks that RX interface packets are properly accounted for"""
        second_vnet = self.vnet_map["vnet2"]
        pipe = second_vnet.pipe

        # Ping neighbor IP on if1 and verify that the counter was incremented
        self.send_object(pipe, "if1")
        old_stat = self.wait_object(pipe)
        assert subprocess.run("ping -c5 2001:db8:a::2".split()).returncode == 0
        self.send_object(pipe, "if1")
        new_stat = self.wait_object(pipe)
        assert new_stat["received-packets"] - old_stat["received-packets"] >= 5

        # Ping neighbor IP on if2 and verify that the counter was incremented
        self.send_object(pipe, "if2")
        old_stat = self.wait_object(pipe)
        assert subprocess.run("ping -c5 2001:db8:b::2".split()).returncode == 0
        self.send_object(pipe, "if2")
        new_stat = self.wait_object(pipe)
        assert new_stat["received-packets"] - old_stat["received-packets"] >= 5