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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.