From: Kent Gibson <warthog618@gmail.com>
To: Bartosz Golaszewski <brgl@bgdev.pl>
Cc: Linus Walleij <linus.walleij@linaro.org>,
Andy Shevchenko <andriy.shevchenko@linux.intel.com>,
Darrien <darrien@freenet.de>,
Viresh Kumar <viresh.kumar@linaro.org>, Jiri Benc <jbenc@upir.cz>,
Joel Savitz <joelsavitz@gmail.com>,
linux-gpio@vger.kernel.org
Subject: Re: [libgpiod v2][PATCH v2 4/5] bindings: python: add tests for v2 API
Date: Tue, 5 Jul 2022 10:08:45 +0800 [thread overview]
Message-ID: <20220705020845.GA6652@sol> (raw)
In-Reply-To: <20220628084226.472035-5-brgl@bgdev.pl>
On Tue, Jun 28, 2022 at 10:42:25AM +0200, Bartosz Golaszewski wrote:
> This adds a python wrapper around libgpiosim and a set of test cases
> for the v2 API using python's standard unittest module.
>
> Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
> ---
> bindings/python/tests/Makefile.am | 14 +
> bindings/python/tests/cases/__init__.py | 12 +
> bindings/python/tests/cases/tests_chip.py | 157 +++++++
> .../python/tests/cases/tests_chip_info.py | 59 +++
> .../python/tests/cases/tests_edge_event.py | 279 +++++++++++
> .../python/tests/cases/tests_info_event.py | 135 ++++++
> .../python/tests/cases/tests_line_config.py | 254 ++++++++++
> .../python/tests/cases/tests_line_info.py | 90 ++++
> .../python/tests/cases/tests_line_request.py | 345 ++++++++++++++
> bindings/python/tests/cases/tests_misc.py | 53 +++
> .../tests/cases/tests_request_config.py | 77 ++++
> bindings/python/tests/gpiod_py_test.py | 25 +
> bindings/python/tests/gpiosimmodule.c | 434 ++++++++++++++++++
> 13 files changed, 1934 insertions(+)
> create mode 100644 bindings/python/tests/Makefile.am
> create mode 100644 bindings/python/tests/cases/__init__.py
> create mode 100644 bindings/python/tests/cases/tests_chip.py
> create mode 100644 bindings/python/tests/cases/tests_chip_info.py
> create mode 100644 bindings/python/tests/cases/tests_edge_event.py
> create mode 100644 bindings/python/tests/cases/tests_info_event.py
> create mode 100644 bindings/python/tests/cases/tests_line_config.py
> create mode 100644 bindings/python/tests/cases/tests_line_info.py
> create mode 100644 bindings/python/tests/cases/tests_line_request.py
> create mode 100644 bindings/python/tests/cases/tests_misc.py
> create mode 100644 bindings/python/tests/cases/tests_request_config.py
> create mode 100755 bindings/python/tests/gpiod_py_test.py
> create mode 100644 bindings/python/tests/gpiosimmodule.c
>
> diff --git a/bindings/python/tests/Makefile.am b/bindings/python/tests/Makefile.am
> new file mode 100644
> index 0000000..099574f
> --- /dev/null
> +++ b/bindings/python/tests/Makefile.am
> @@ -0,0 +1,14 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +# SPDX-FileCopyrightText: 2017-2021 Bartosz Golaszewski <bartekgola@gmail.com>
> +
It is 2022?
Which email address are you going with? gmail here and bgdev below.
> +dist_bin_SCRIPTS = gpiod_py_test.py
> +
> +pyexec_LTLIBRARIES = gpiosim.la
> +
> +gpiosim_la_SOURCES = gpiosimmodule.c
> +gpiosim_la_CFLAGS = -I$(top_srcdir)/tests/gpiosim/
> +gpiosim_la_CFLAGS += -Wall -Wextra -g -std=gnu89 $(PYTHON_CPPFLAGS)
> +gpiosim_la_LDFLAGS = -module -avoid-version
> +gpiosim_la_LIBADD = $(top_builddir)/tests/gpiosim/libgpiosim.la
> +gpiosim_la_LIBADD += $(top_builddir)/bindings/python/enum/libpycenum.la
> +gpiosim_la_LIBADD += $(PYTHON_LIBS)
> diff --git a/bindings/python/tests/cases/__init__.py b/bindings/python/tests/cases/__init__.py
> new file mode 100644
> index 0000000..6503663
> --- /dev/null
> +++ b/bindings/python/tests/cases/__init__.py
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
> +
> +from .tests_chip import *
> +from .tests_chip_info import *
> +from .tests_edge_event import *
> +from .tests_info_event import *
> +from .tests_line_config import *
> +from .tests_line_info import *
> +from .tests_line_request import *
> +from .tests_misc import *
> +from .tests_request_config import *
> diff --git a/bindings/python/tests/cases/tests_chip.py b/bindings/python/tests/cases/tests_chip.py
> new file mode 100644
> index 0000000..844dbfc
> --- /dev/null
> +++ b/bindings/python/tests/cases/tests_chip.py
[snip]
> +class WaitingForEdgeEvents(unittest.TestCase):
> + def setUp(self):
> + self.sim = gpiosim.Chip(num_lines=8)
> + self.thread = None
> +
> + def tearDown(self):
> + if self.thread:
> + self.thread.join()
> + self.sim = None
> +
> + def trigger_falling_and_rising_edge(self, offset):
> + time.sleep(0.05)
> + self.sim.set_pull(offset, Pull.PULL_UP)
> + time.sleep(0.05)
> + self.sim.set_pull(offset, Pull.PULL_DOWN)
> +
> + def trigger_rising_edge_events_on_two_offsets(self, offset0, offset1):
> + time.sleep(0.05)
> + self.sim.set_pull(offset0, Pull.PULL_UP)
> + time.sleep(0.05)
> + self.sim.set_pull(offset1, Pull.PULL_UP)
> +
> + def test_both_edge_events(self):
> + with gpiod.request_lines(
> + self.sim.dev_path,
> + gpiod.RequestConfig(offsets=[2]),
> + gpiod.LineConfig(edge_detection=Edge.BOTH),
> + ) as req:
> + buf = gpiod.EdgeEventBuffer()
> + self.thread = threading.Thread(
> + target=partial(self.trigger_falling_and_rising_edge, 2)
> + )
> + self.thread.start()
> +
> + self.assertTrue(req.wait_edge_event(datetime.timedelta(seconds=1)))
> + self.assertEqual(req.read_edge_event(buf), 1)
> + self.assertEqual(len(buf), 1)
> + event = buf[0]
> + self.assertEqual(event.type, EventType.RISING_EDGE)
> + self.assertEqual(event.line_offset, 2)
> + ts_rising = event.timestamp_ns
> +
> + self.assertTrue(req.wait_edge_event(datetime.timedelta(seconds=1)))
> + self.assertEqual(req.read_edge_event(buf), 1)
> + self.assertEqual(len(buf), 1)
> + event = buf[0]
> + self.assertEqual(event.type, EventType.FALLING_EDGE)
> + self.assertEqual(event.line_offset, 2)
> + ts_falling = event.timestamp_ns
> +
> + self.assertGreater(ts_falling, ts_rising)
> +
> + def test_rising_edge_event(self):
> + with gpiod.request_lines(
> + self.sim.dev_path,
> + gpiod.RequestConfig(offsets=[6]),
> + gpiod.LineConfig(edge_detection=Edge.RISING),
> + ) as req:
> + buf = gpiod.EdgeEventBuffer()
> + self.thread = threading.Thread(
> + target=partial(self.trigger_falling_and_rising_edge, 6)
> + )
> + self.thread.start()
> +
> + self.assertTrue(req.wait_edge_event(datetime.timedelta(seconds=1)))
> + self.assertEqual(req.read_edge_event(buf), 1)
> + self.assertEqual(len(buf), 1)
> + event = buf[0]
> + self.assertEqual(event.type, EventType.RISING_EDGE)
> + self.assertEqual(event.line_offset, 6)
> +
> + self.assertFalse(
> + req.wait_edge_event(datetime.timedelta(microseconds=10000))
> + )
> +
> + def test_falling_edge_event(self):
> + with gpiod.request_lines(
> + self.sim.dev_path,
> + gpiod.RequestConfig(offsets=[6]),
> + gpiod.LineConfig(edge_detection=Edge.FALLING),
> + ) as req:
> + buf = gpiod.EdgeEventBuffer()
> + self.thread = threading.Thread(
> + target=partial(self.trigger_falling_and_rising_edge, 6)
> + )
> + self.thread.start()
> +
Benefit of the thread? (and elsewhere a background thread is used)
The sleeps therein are only necessary because it is run in the
background.
> + self.assertTrue(req.wait_edge_event(datetime.timedelta(seconds=1)))
> + self.assertEqual(req.read_edge_event(buf), 1)
> + self.assertEqual(len(buf), 1)
> + event = buf[0]
> + self.assertEqual(event.type, EventType.FALLING_EDGE)
> + self.assertEqual(event.line_offset, 6)
> +
> + self.assertFalse(
> + req.wait_edge_event(datetime.timedelta(microseconds=10000))
> + )
> +
> + def test_sequence_numbers(self):
> + with gpiod.request_lines(
> + self.sim.dev_path,
> + gpiod.RequestConfig(offsets=[2, 4]),
> + gpiod.LineConfig(edge_detection=Edge.BOTH),
> + ) as req:
> + buf = gpiod.EdgeEventBuffer()
> + self.thread = threading.Thread(
> + target=partial(self.trigger_rising_edge_events_on_two_offsets, 2, 4)
> + )
> + self.thread.start()
> +
> + self.assertTrue(req.wait_edge_event(datetime.timedelta(seconds=1)))
> + self.assertEqual(req.read_edge_event(buf), 1)
> + self.assertEqual(len(buf), 1)
> + event = buf[0]
> + self.assertEqual(event.type, EventType.RISING_EDGE)
> + self.assertEqual(event.line_offset, 2)
> + self.assertEqual(event.global_seqno, 1)
> + self.assertEqual(event.line_seqno, 1)
> +
> + self.assertTrue(req.wait_edge_event(datetime.timedelta(seconds=1)))
> + self.assertEqual(req.read_edge_event(buf), 1)
> + self.assertEqual(len(buf), 1)
> + event = buf[0]
> + self.assertEqual(event.type, EventType.RISING_EDGE)
> + self.assertEqual(event.line_offset, 4)
> + self.assertEqual(event.global_seqno, 2)
> + self.assertEqual(event.line_seqno, 1)
> +
> +
[snip]
> +++ b/bindings/python/tests/cases/tests_line_request.py
> @@ -0,0 +1,345 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
> +
> +import errno
> +import gpiod
> +import gpiosim
> +import unittest
> +
> +
> +Direction = gpiod.Line.Direction
> +Edge = gpiod.Line.Edge
> +Bias = gpiod.Line.Bias
> +Value = gpiod.Line.Value
> +SimVal = gpiosim.Chip.Value
> +Pull = gpiosim.Chip.Pull
> +
> +
> +class LineRequestConstructor(unittest.TestCase):
> + def test_line_request_cannot_be_instantiated(self):
> + with self.assertRaises(TypeError):
> + info = gpiod.LineRequest()
> +
> +
> +class ChipLineRequestWorks(unittest.TestCase):
> + def test_chip_line_request(self):
> + sim = gpiosim.Chip()
> +
> + with gpiod.Chip(sim.dev_path) as chip:
> + with chip.request_lines(
> + gpiod.RequestConfig(offsets=[0]), gpiod.LineConfig()
> + ) as req:
> + pass
> +
> +
> +class ModuleLineRequestWorks(unittest.TestCase):
Put module level tests in a module level file, say tests_module.py?
All the tests in this file should test LineRequest methods.
(i.e. where request_lines() has succeeded so the req is constructed)
Chip.request_lines() tests should be in tests_chip.py.
Particularly the failure cases.
> + def test_module_line_request(self):
> + sim = gpiosim.Chip()
> +
> + with gpiod.request_lines(
> + sim.dev_path, gpiod.RequestConfig(offsets=[0]), gpiod.LineConfig()
> + ) as req:
> + pass
> +
> + def test_module_line_request_lines_arg(self):
> + sim = gpiosim.Chip(num_lines=16, line_names={0: "foo", 2: "bar", 5: "xyz"})
> +
> + with gpiod.request_lines(sim.dev_path, lines=["foo", "bar", "xyz"]) as req:
> + self.assertEqual(req.offsets, [0, 2, 5])
> +
> + with gpiod.request_lines(sim.dev_path, lines=["foo", 9, "xyz", 12]) as req:
> + self.assertEqual(req.offsets, [0, 9, 5, 12])
> +
Test name mapping failures.
Test lines=[].
Test lines=None.
> + def test_module_line_request_direction(self):
> + sim = gpiosim.Chip(num_lines=2)
> +
> + with gpiod.request_lines(
> + sim.dev_path, lines=[0, 1], direction=Direction.OUTPUT
> + ) as req:
> + with gpiod.Chip(sim.dev_path) as chip:
> + info = chip.get_line_info(0)
> + self.assertEqual(info.direction, Direction.OUTPUT)
> + self.assertTrue(info.used)
> +
> + def test_module_line_request_edge_detection(self):
> + sim = gpiosim.Chip()
> +
> + with gpiod.request_lines(
> + sim.dev_path, lines=[0], edge_detection=Edge.BOTH
> + ) as req:
> + sim.set_pull(0, Pull.PULL_UP)
> + self.assertTrue(req.wait_edge_event())
> + self.assertEqual(req.read_edge_event()[0].line_offset, 0)
> +
> +
> +class RequestingLinesFailsWithInvalidArguments(unittest.TestCase):
These tests should be in tests_chip.py, as they are testing the
Chip.request_lines() method.
And they should have module level equivalents (don't assume one wraps
the other).
> + def setUp(self):
> + self.sim = gpiosim.Chip(num_lines=8)
> + self.chip = gpiod.Chip(self.sim.dev_path)
> +
> + def tearDown(self):
> + self.chip.close()
> + self.chip = None
> + self.sim = None
> +
> + def test_passing_invalid_types_as_configs(self):
> + with self.assertRaises(TypeError):
> + self.chip.request_lines("foobar", gpiod.LineConfig())
> +
> + with self.assertRaises(TypeError):
> + self.chip.request_lines(gpiod.RequestConfig(offsets=[0]), "foobar")
> +
> + def test_no_offsets(self):
> + with self.assertRaises(ValueError):
> + self.chip.request_lines(gpiod.RequestConfig(), gpiod.LineConfig())
> +
> + def test_duplicate_offsets(self):
> + with self.assertRaises(OSError) as ex:
> + self.chip.request_lines(
> + gpiod.RequestConfig(offsets=[2, 5, 1, 7, 5]), gpiod.LineConfig()
> + )
> +
> + self.assertEqual(ex.exception.errno, errno.EBUSY)
> +
> + def test_offset_out_of_range(self):
> + with self.assertRaises(ValueError):
> + self.chip.request_lines(
> + gpiod.RequestConfig(offsets=[1, 0, 4, 8]), gpiod.LineConfig()
> + )
> +
[snip]
> +++ b/bindings/python/tests/cases/tests_misc.py
> @@ -0,0 +1,53 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +# SPDX-FileCopyrightText: 2022 Bartosz Golaszewski <brgl@bgdev.pl>
> +
The tests in this file are all module scope, and cover functions from
module.c, so rename to tests_module.py.
> +import gpiod
> +import gpiosim
> +import os
> +import re
> +import unittest
> +
> +
> +class LinkGuard:
> + def __init__(self, src, dst):
> + self.src = src
> + self.dst = dst
> +
> + def __enter__(self):
> + os.symlink(self.src, self.dst)
> +
> + def __exit__(self, type, val, tb):
> + os.unlink(self.dst)
> +
> +
> +class IsGPIOChip(unittest.TestCase):
> + def test_is_gpiochip_bad(self):
> + self.assertFalse(gpiod.is_gpiochip_device("/dev/null"))
> + self.assertFalse(gpiod.is_gpiochip_device("/dev/nonexistent"))
> +
> + def test_is_gpiochip_good(self):
> + sim = gpiosim.Chip()
> +
> + self.assertTrue(gpiod.is_gpiochip_device(sim.dev_path))
> +
> + def test_is_gpiochip_link_good(self):
> + link = "/tmp/gpiod-py-test-link.{}".format(os.getpid())
> + sim = gpiosim.Chip()
> +
> + with LinkGuard(sim.dev_path, link):
> + self.assertTrue(gpiod.is_gpiochip_device(link))
> +
> + def test_is_gpiochip_link_bad(self):
> + link = "/tmp/gpiod-py-test-link.{}".format(os.getpid())
> +
> + with LinkGuard("/dev/null", link):
> + self.assertFalse(gpiod.is_gpiochip_device(link))
> +
> +
> +class VersionString(unittest.TestCase):
> + def test_version_string(self):
> + self.assertTrue(
> + re.match(
> + "^[0-9][1-9]?\\.[0-9][1-9]?([\\.0-9]?|\\-devel)$", gpiod.__version__
> + )
> + )
> diff --git a/bindings/python/tests/cases/tests_request_config.py b/bindings/python/tests/cases/tests_request_config.py
[snip]
A complete audit of the test coverage would be beneficial.
I haven't attempted that - only pointed out any gaps I happened to notice.
Are there any coverage tools available for Python C modules?
Cheers,
Kent.
next prev parent reply other threads:[~2022-07-05 2:08 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-28 8:42 [libgpiod v2][PATCH v2 0/5] bindings: implement python bindings for libgpiod v2 Bartosz Golaszewski
2022-06-28 8:42 ` [libgpiod v2][PATCH v2 1/5] bindings: python: remove old version Bartosz Golaszewski
2022-06-28 8:42 ` [libgpiod v2][PATCH v2 2/5] bindings: python: enum: add a piece of common code for using python's enums from C Bartosz Golaszewski
2022-06-28 8:42 ` [libgpiod v2][PATCH v2 3/5] bindings: python: add examples for v2 API Bartosz Golaszewski
2022-06-28 8:42 ` [libgpiod v2][PATCH v2 4/5] bindings: python: add tests " Bartosz Golaszewski
2022-07-05 2:08 ` Kent Gibson [this message]
2022-07-07 10:17 ` Bartosz Golaszewski
2022-07-07 12:22 ` Kent Gibson
2022-06-28 8:42 ` [libgpiod v2][PATCH v2 5/5] bindings: python: add the implementation " Bartosz Golaszewski
2022-06-30 2:25 ` Kent Gibson
2022-06-30 6:54 ` Bartosz Golaszewski
2022-06-30 8:14 ` Kent Gibson
2022-06-30 8:38 ` Kent Gibson
2022-07-01 6:07 ` Kent Gibson
2022-07-01 7:21 ` Bartosz Golaszewski
2022-07-01 7:26 ` Kent Gibson
2022-07-01 7:29 ` Bartosz Golaszewski
2022-07-01 7:33 ` Kent Gibson
2022-07-01 8:02 ` Kent Gibson
2022-07-01 8:18 ` Bartosz Golaszewski
2022-07-01 8:32 ` Bartosz Golaszewski
2022-07-01 8:52 ` Kent Gibson
2022-07-01 9:28 ` Bartosz Golaszewski
2022-07-01 8:32 ` Kent Gibson
2022-07-05 2:09 ` Kent Gibson
2022-07-07 12:19 ` Bartosz Golaszewski
2022-07-07 13:09 ` Kent Gibson
2022-07-07 20:09 ` Bartosz Golaszewski
2022-07-08 1:38 ` Kent Gibson
2022-07-08 9:49 ` Bartosz Golaszewski
2022-07-08 10:56 ` Kent Gibson
2022-07-08 11:28 ` Bartosz Golaszewski
2022-07-08 15:26 ` Bartosz Golaszewski
2022-07-08 15:58 ` Kent Gibson
2022-06-28 8:47 ` [libgpiod v2][PATCH v2 0/5] bindings: implement python bindings for libgpiod v2 Bartosz Golaszewski
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=20220705020845.GA6652@sol \
--to=warthog618@gmail.com \
--cc=andriy.shevchenko@linux.intel.com \
--cc=brgl@bgdev.pl \
--cc=darrien@freenet.de \
--cc=jbenc@upir.cz \
--cc=joelsavitz@gmail.com \
--cc=linus.walleij@linaro.org \
--cc=linux-gpio@vger.kernel.org \
--cc=viresh.kumar@linaro.org \
/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;
as well as URLs for NNTP newsgroup(s).