On 3/11/22 16:21, Adrian Moreno wrote:
> Use pytest to run unit tests as part of the standard testsuite.
> 
> Acked-by: Eelco Chaudron <[email protected]>
> Signed-off-by: Adrian Moreno <[email protected]>
> ---
>  .github/workflows/build-and-test.yml    |  3 +
>  Documentation/intro/install/general.rst |  4 ++
>  python/automake.mk                      |  9 ++-
>  python/ovs/tests/test_kv.py             | 76 +++++++++++++++++++++++++
>  python/test_requirements.txt            |  3 +
>  tests/atlocal.in                        | 19 +++++++
>  tests/automake.mk                       |  1 +
>  tests/pytest.at                         |  7 +++
>  tests/testsuite.at                      |  1 +
>  9 files changed, 121 insertions(+), 2 deletions(-)
>  create mode 100644 python/ovs/tests/test_kv.py
>  create mode 100644 python/test_requirements.txt
>  create mode 100644 tests/pytest.at
> 
> diff --git a/.github/workflows/build-and-test.yml 
> b/.github/workflows/build-and-test.yml
> index eac3504e4..44df1c2d5 100644
> --- a/.github/workflows/build-and-test.yml
> +++ b/.github/workflows/build-and-test.yml
> @@ -123,6 +123,9 @@ jobs:
>        with:
>          python-version: '3.9'
>  
> +    - name: install python dependencies
> +      run: pip install -r python/test_requirements.txt

Can we place this after the 'prepare' stage or inside the
prepare.sh script?  Just to ensure that we have 'wheel'
installed before trying to install anything else.

> +
>      - name: create ci signature file for the dpdk cache key
>        if:   matrix.dpdk != '' || matrix.dpdk_shared != ''
>        # This will collect most of DPDK related lines, so hash will be 
> different
> diff --git a/Documentation/intro/install/general.rst 
> b/Documentation/intro/install/general.rst
> index c4300cd53..711fb98a4 100644
> --- a/Documentation/intro/install/general.rst
> +++ b/Documentation/intro/install/general.rst
> @@ -181,6 +181,10 @@ following to obtain better warnings:
>    come from the "hacking" flake8 plugin. If it's not installed, the warnings
>    just won't occur until it's run on a system with "hacking" installed.
>  
> +- the python packages listed in "python/test_requirements.txt" (compatible
> +  with pip). If they are installed, the pytest-based Python unit tests will
> +  be run.
> +
>  You may find the ovs-dev script found in ``utilities/ovs-dev.py`` useful.
>  
>  .. _general-install-reqs:
> diff --git a/python/automake.mk b/python/automake.mk
> index e5501c58e..43a27d3cb 100644
> --- a/python/automake.mk
> +++ b/python/automake.mk
> @@ -53,6 +53,9 @@ ovs_pyfiles = \
>       python/ovs/vlog.py \
>       python/ovs/winutils.py
>  
> +ovs_pytests = \
> +     python/ovs/tests/test_kv.py
> +
>  # These python files are used at build time but not runtime,
>  # so they are not installed.
>  EXTRA_DIST += \
> @@ -66,12 +69,14 @@ EXTRA_DIST += \
>  EXTRA_DIST += \
>       python/ovs/compat/sortedcontainers/LICENSE \
>       python/README.rst \
> -     python/setup.py
> +     python/setup.py \
> +     python/test_requirements.txt
>  
>  # C extension support.
>  EXTRA_DIST += python/ovs/_json.c
>  
> -PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles)
> +PYFILES = $(ovs_pyfiles) python/ovs/dirs.py $(ovstest_pyfiles) $(ovs_pytests)
> +
>  EXTRA_DIST += $(PYFILES)
>  PYCOV_CLEAN_FILES += $(PYFILES:.py=.py,cover)
>  
> diff --git a/python/ovs/tests/test_kv.py b/python/ovs/tests/test_kv.py
> new file mode 100644
> index 000000000..e81804d49
> --- /dev/null
> +++ b/python/ovs/tests/test_kv.py
> @@ -0,0 +1,76 @@
> +import pytest
> +
> +from ovs.flows.kv import KVParser, KeyValue
> +
> +
> [email protected](
> +    "input_data,expected",
> +    [
> +        (
> +            (
> +                "cookie=0x0, duration=147566.365s, table=0, n_packets=39, 
> n_bytes=2574, idle_age=65534, hard_age=65534",  # noqa: E501
> +                None,
> +            ),
> +            [
> +                KeyValue("cookie", 0),
> +                KeyValue("duration", "147566.365s"),
> +                KeyValue("table", 0),
> +                KeyValue("n_packets", 39),
> +                KeyValue("n_bytes", 2574),
> +                KeyValue("idle_age", 65534),
> +                KeyValue("hard_age", 65534),
> +            ],
> +        ),
> +        (
> +            (
> +                
> "load:0x4->NXM_NX_REG13[],load:0x9->NXM_NX_REG11[],load:0x8->NXM_NX_REG12[],load:0x1->OXM_OF_METADATA[],load:0x1->NXM_NX_REG14[],mod_dl_src:0a:58:a9:fe:00:02,resubmit(,8)",
>   # noqa: E501
> +                None,
> +            ),
> +            [
> +                KeyValue("load", "0x4->NXM_NX_REG13[]"),
> +                KeyValue("load", "0x9->NXM_NX_REG11[]"),
> +                KeyValue("load", "0x8->NXM_NX_REG12[]"),
> +                KeyValue("load", "0x1->OXM_OF_METADATA[]"),
> +                KeyValue("load", "0x1->NXM_NX_REG14[]"),
> +                KeyValue("mod_dl_src", "0a:58:a9:fe:00:02"),
> +                KeyValue("resubmit", ",8"),
> +            ],
> +        ),
> +        (
> +            ("l1(l2(l3(l4())))", None),
> +            [KeyValue("l1", "l2(l3(l4()))")]
> +        ),
> +        (
> +            ("l1(l2(l3(l4()))),foo:bar", None),
> +            [KeyValue("l1", "l2(l3(l4()))"), KeyValue("foo", "bar")],
> +        ),
> +        (
> +            ("enqueue:1:2,output=2", None),
> +            [KeyValue("enqueue", "1:2"), KeyValue("output", 2)],
> +        ),
> +        (
> +            ("value_to_reg(100)->someReg[10],foo:bar", None),
> +            [
> +                KeyValue("value_to_reg", "(100)->someReg[10]"),
> +                KeyValue("foo", "bar"),
> +            ],
> +        ),
> +    ],
> +)
> +def test_kv_parser(input_data, expected):
> +    input_string = input_data[0]
> +    decoders = input_data[1]
> +    tparser = KVParser(input_string, decoders)
> +    tparser.parse()
> +    result = tparser.kv()
> +    assert len(expected) == len(result)
> +    for i in range(0, len(result)):
> +        assert result[i].key == expected[i].key
> +        assert result[i].value == expected[i].value
> +        kpos = result[i].meta.kpos
> +        kstr = result[i].meta.kstring
> +        vpos = result[i].meta.vpos
> +        vstr = result[i].meta.vstring
> +        assert input_string[kpos : kpos + len(kstr)] == kstr
> +        if vpos != -1:
> +            assert input_string[vpos : vpos + len(vstr)] == vstr
> diff --git a/python/test_requirements.txt b/python/test_requirements.txt
> new file mode 100644
> index 000000000..6aaee13e3
> --- /dev/null
> +++ b/python/test_requirements.txt
> @@ -0,0 +1,3 @@
> +pytest
> +netaddr
> +pyparsing
> diff --git a/tests/atlocal.in b/tests/atlocal.in
> index a0ad239ec..2426416a0 100644
> --- a/tests/atlocal.in
> +++ b/tests/atlocal.in
> @@ -222,3 +222,22 @@ export OVS_CTL_TIMEOUT
>  # matter break everything.
>  ASAN_OPTIONS=detect_leaks=0:abort_on_error=true:log_path=asan:$ASAN_OPTIONS
>  export ASAN_OPTIONS
> +
> +# Check whether Python test requirements are available.
> +REQUIREMENT_PATH=$abs_top_srcdir/python/test_requirements.txt $PYTHON3 -c '
> +import os
> +import pathlib
> +import pkg_resources

The 'import sys' is missing here.  Without it, the test fails instead
of being skipped:

Traceback (most recent call last):
  File "<string>", line 9, in <module>
  File "/usr/lib/python3.9/site-packages/pkg_resources/__init__.py", line 886, 
in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/lib/python3.9/site-packages/pkg_resources/__init__.py", line 772, 
in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'pytest' distribution was not found and 
is required by the application

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 11, in <module>
NameError: name 'sys' is not defined
./tests/testsuite: unexpected error probing Python unit test requirements
## ------------------------------- ##
## openvswitch 2.17.90 test suite. ##
## ------------------------------- ##
2497: Pytest unit tests - Python3                     FAILED (pytest.at:6)

> +
> +with pathlib.Path(os.path.join(os.getenv("REQUIREMENT_PATH"))).open() as 
> reqs:
> +    for req in pkg_resources.parse_requirements(reqs):
> +        try:
> +            pkg_resources.require(str(req))
> +        except pkg_resources.DistributionNotFound:
> +            sys.exit(2)
> +'
> +case $? in
> +    0) HAVE_PYTEST=yes ;;
> +    2) HAVE_PYTEST=no ;;
> +    *) echo "$0: unexpected error probing Python unit test requirements" >&2 
> ;;
> +esac
> diff --git a/tests/automake.mk b/tests/automake.mk
> index 0279585cd..e7ffc2691 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -103,6 +103,7 @@ TESTSUITE_AT = \
>       tests/ovsdb-rbac.at \
>       tests/ovs-vsctl.at \
>       tests/ovs-xapi-sync.at \
> +     tests/pytest.at \
>       tests/stp.at \
>       tests/rstp.at \
>       tests/interface-reconfigure.at \
> diff --git a/tests/pytest.at b/tests/pytest.at
> new file mode 100644
> index 000000000..44a88ed98
> --- /dev/null
> +++ b/tests/pytest.at
> @@ -0,0 +1,7 @@
> +AT_BANNER([Python unit tests])
> +
> +# Run pytest unit tests.
> +AT_SETUP([Pytest unit tests - Python3])

Would be nice to have a 'python' keywork defined, so the test
will run with TESTSUITEFLAGS='-k python'.

> +AT_SKIP_IF([test "$HAVE_PYTEST" = "no"])
> +AT_CHECK([python -m pytest $top_srcdir/python/ovs],[0], [ignore], [ignore])

There should be '$PYTHON3' instead of just 'python'.

> +AT_CLEANUP()
> diff --git a/tests/testsuite.at b/tests/testsuite.at
> index 58adfa09c..14a28b517 100644
> --- a/tests/testsuite.at
> +++ b/tests/testsuite.at
> @@ -78,3 +78,4 @@ m4_include([tests/mcast-snooping.at])
>  m4_include([tests/packet-type-aware.at])
>  m4_include([tests/nsh.at])
>  m4_include([tests/drop-stats.at])
> +m4_include([tests/pytest.at])

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to