Tests
=====

Test harness
------------

The test harness is a set of convenience functions used to ease testing of ``bpfilter``.

Test
~~~~
.. doxygenfile:: test.h

Mocks
~~~~~
.. doxygenfile:: mock.h

Process
~~~~~~~
.. doxygenfile:: process.h

Daemon
~~~~~~~
.. doxygenfile:: daemon.h

.. _tests-filters-label:

Filters
~~~~~~~
.. doxygenfile:: filters.h

Program
~~~~~~~
.. doxygenfile:: prog.h


Unit tests
----------

.. warning::

    In progress.


End-to-end tests
----------------

.. note::

    The end-to-end test suite is not yet part of the ``make test`` global tests target. If the Python Scapy module is available on your system, you can run it using ``make e2e``.

End-to-end tests are designed to validate the bytecode generated by ``bpfilter``, which unit and manual tests can't do efficiently. The end-to-end tests harness will start a fresh ``bpfilter`` daemon for every test, generate a new chain, and use ``BPF_PROG_TEST_RUN`` to validate the expected behaviour. The test packets fed to the tested BPF programs are generated by the Python Scapy module (see ``tests/e2e/genpkts.py``).

Each test is run for every available hook automatically, so the BPF bytecode generated for each flavour can be validated to ensure consistent behaviour.

Similarly to the benchmarks (``make benchmark``), ``sudo`` will be used automatically if the current user doesn't have ``root`` privileges.

**Adding a new end-to-end test**

End-to-end tests are defined in ``tests/e2e`` and use ``cmocka`` as the testing library. To add a new end-to-end test:

1. Add a new ``cmocka`` test in a source file under ``tests/e2e``.
2. Create a chain: use the primitives in :ref:`tests-filters-label` to easily create chains, rules, and matchers. :c:struct:`bf_test_chain_get` will automatically disable attachment of the chain and generate a custom name for the BPF program prefixed with ``bf_e2e_``.
3. Run the test using :c:func:`bft_e2e_test` with the chain, the expected return value, and the generated packet name.

**Example**

The example below will create an empty chain with a default ``ACCEPT`` policy.

.. note::

    While the following example defines a chain for the XDP hook, it will be overridden by :c:func:`bft_e2e_test` to be tested for every hook.

.. code-block:: c

    Test(policy, accept_no_rule)
    {
        _free_bf_chain_ struct bf_chain *chain = bf_test_chain_get(
            BF_HOOK_XDP,
            BF_VERDICT_ACCEPT,
            NULL,
            (struct bf_rule *[]) {
                NULL,
            }
        );

        bft_e2e_test(chain, BF_VERDICT_ACCEPT, pkt_local_ip6_tcp);
    }


Build tests
-----------

Build tests are designed to validate bpfilter integration to other projects. By including and linking libbpfilter and core module to dummy C and C++ source files, and building those files using strict compiler flags, we ensure bpfilter doesn't rely on compiler extensions or exposes non standard features.


Integration tests
-----------------

``bpfilter``'s repository contains patches to add support for ``bpfilter`` to ``nftables`` and ``iptables``. You first need to install ``nftables``' and ``iptables``' build dependencies:

.. code-block:: shell

    # Fedora 40+
    sudo dnf install -y autoconf automake gmp-devel libtool libedit-devel libmnl-devel libnftnl-devel

    # Ubuntu 24.04
    sudo apt-get install -y autoconf bison flex libedit-dev libgmp-dev libmnl-dev libnftnl-dev libtool

Then, you can build both from ``bpfilter``'s build directory:

.. code-block:: shell

    make -C $BUILD_DIRECTORY integration

Once this command succeeds, ``nft`` (``nftables``'s command-line tool) and ``iptables`` are available in ``$BUILD_DIRECTORY/tools/install``.

With either ``nft`` or ``iptables``, you can now communicate directly with the ``bpfilter`` daemon instead of the kernel by using the ``--bpf`` flag. This allows your filtering rules to be translated into BPF programs by ``bpfilter``.
