From 452696601ae5e1b1498fe1109cff2146e5424514 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 7 Aug 2019 14:28:30 +0200 Subject: [PATCH] bindings: python: use unittest to implement a proper test-suite Currently the only tests we have for python bindings are in a simple script that makes a lot of assumptions about the environment and doesn't even work anymore with recent kernels. Remove it and replace it with a proper test-suite implemented using libgpiod and the standard python unittest package. Signed-off-by: Bartosz Golaszewski --- TODO | 9 - bindings/python/Makefile.am | 6 + bindings/python/examples/Makefile.am | 3 +- bindings/python/examples/gpiod_tests.py | 437 --------------- bindings/python/tests/Makefile.am | 18 + bindings/python/tests/gpiod_py_test.py | 650 +++++++++++++++++++++++ bindings/python/tests/gpiomockupmodule.c | 313 +++++++++++ configure.ac | 1 + 8 files changed, 989 insertions(+), 448 deletions(-) delete mode 100755 bindings/python/examples/gpiod_tests.py create mode 100644 bindings/python/tests/Makefile.am create mode 100755 bindings/python/tests/gpiod_py_test.py create mode 100644 bindings/python/tests/gpiomockupmodule.c diff --git a/TODO b/TODO index e9862d0..654fff6 100644 --- a/TODO +++ b/TODO @@ -25,15 +25,6 @@ and is partially functional. ---------- -* use the python unit testing library for python bindings and reuse - libgpiod-test - -The best candidate for the testing framework is the standard unittest module -available in cpython. We'd need however to wrap libgpiod-test in a C module -for python just like we did for the bindings of the core C library. - ----------- - * implement a simple daemon for controlling GPIOs in C together with a client program diff --git a/bindings/python/Makefile.am b/bindings/python/Makefile.am index 2c00c6e..5b33857 100644 --- a/bindings/python/Makefile.am +++ b/bindings/python/Makefile.am @@ -16,3 +16,9 @@ gpiod_la_CFLAGS = -I$(top_srcdir)/include/ gpiod_la_CFLAGS += -Wall -Wextra -g $(PYTHON_CPPFLAGS) gpiod_la_LDFLAGS = -module -avoid-version gpiod_la_LIBADD = $(top_builddir)/lib/libgpiod.la $(PYTHON_LIBS) + +if WITH_TESTS + +SUBDIRS += tests + +endif diff --git a/bindings/python/examples/Makefile.am b/bindings/python/examples/Makefile.am index 2745718..0703e81 100644 --- a/bindings/python/examples/Makefile.am +++ b/bindings/python/examples/Makefile.am @@ -6,8 +6,7 @@ # Copyright (C) 2017-2018 Bartosz Golaszewski # -EXTRA_DIST = gpiod_tests.py \ - gpiodetect.py \ +EXTRA_DIST = gpiodetect.py \ gpiofind.py \ gpioget.py \ gpioinfo.py \ diff --git a/bindings/python/examples/gpiod_tests.py b/bindings/python/examples/gpiod_tests.py deleted file mode 100755 index 910613a..0000000 --- a/bindings/python/examples/gpiod_tests.py +++ /dev/null @@ -1,437 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: LGPL-2.1-or-later - -# -# This file is part of libgpiod. -# -# Copyright (C) 2017-2018 Bartosz Golaszewski -# - -'''Misc tests of libgpiod python bindings. - -These tests assume that at least one dummy gpiochip is present in the -system and that it's detected as gpiochip0. -''' - -import gpiod -import select - -test_cases = [] - -def add_test(name, func): - global test_cases - - test_cases.append((name, func)) - -def fire_line_event(chip, offset, rising): - path = '/sys/kernel/debug/gpio-mockup-event/{}/{}'.format(chip, offset) - with open(path, 'w') as fd: - fd.write('{}'.format(1 if rising else 0)) - -def print_event(event): - print('type: {}'.format('rising' if event.type == gpiod.LineEvent.RISING_EDGE else 'falling')) - print('timestamp: {}.{}'.format(event.sec, event.nsec)) - print('source line offset: {}'.format(event.source.offset())) - -def chip_open_default_lookup(): - by_name = gpiod.Chip('gpiochip0') - by_path = gpiod.Chip('/dev/gpiochip0') - by_label = gpiod.Chip('gpio-mockup-A') - by_number = gpiod.Chip('0') - print('All good') - by_name.close() - by_path.close() - by_label.close() - by_number.close() - -add_test('Open a GPIO chip using different lookup modes', chip_open_default_lookup) - -def chip_open_different_modes(): - chip = gpiod.Chip('/dev/gpiochip0', gpiod.Chip.OPEN_BY_PATH) - chip.close() - chip = gpiod.Chip('gpiochip0', gpiod.Chip.OPEN_BY_NAME) - chip.close() - chip = gpiod.Chip('gpio-mockup-A', gpiod.Chip.OPEN_BY_LABEL) - chip.close() - chip = gpiod.Chip('0', gpiod.Chip.OPEN_BY_NUMBER) - chip.close() - print('All good') - -add_test('Open a GPIO chip using different modes', chip_open_different_modes) - -def chip_open_nonexistent(): - try: - chip = gpiod.Chip('/nonexistent_gpiochip') - except OSError as ex: - print('Exception raised as expected: {}'.format(ex)) - return - - assert False, 'OSError expected' - -add_test('Try to open a nonexistent GPIO chip', chip_open_nonexistent) - -def chip_open_no_args(): - try: - chip = gpiod.Chip() - except TypeError: - print('Error as expected') - return - - assert False, 'TypeError expected' - -add_test('Open a GPIO chip without arguments', chip_open_no_args) - -def chip_use_after_close(): - chip = gpiod.Chip('gpiochip0') - line = chip.get_line(2) - chip.close() - - try: - chip.name() - except ValueError as ex: - print('Error as expected: {}'.format(ex)) - - try: - line = chip.get_line(3) - except ValueError as ex: - print('Error as expected: {}'.format(ex)) - return - - assert False, 'ValueError expected' - -add_test('Use a GPIO chip after closing it', chip_use_after_close) - -def chip_with_statement(): - with gpiod.Chip('gpiochip0') as chip: - print('Chip name in controlled execution: {}'.format(chip.name())) - line = chip.get_line(3) - print('Got line from chip in controlled execution: {}'.format(line.name())) - -add_test('Use a GPIO chip in controlled execution', chip_with_statement) - -def chip_info(): - chip = gpiod.Chip('gpiochip0') - print('name: {}'.format(chip.name())) - print('label: {}'.format(chip.label())) - print('lines: {}'.format(chip.num_lines())) - chip.close() - -add_test('Print chip info', chip_info) - -def print_chip(): - chip = gpiod.Chip('/dev/gpiochip0') - print(chip) - chip.close() - -add_test('Print chip object', print_chip) - -def create_chip_without_arguments(): - try: - chip = gpiod.Chip() - except TypeError as ex: - print('Exception raised as expected: {}'.format(ex)) - return - - assert False, 'TypeError expected' - -add_test('Create chip object without arguments', create_chip_without_arguments) - -def create_line_object(): - try: - line = gpiod.Line() - except NotImplementedError: - print('Error as expected') - return - - assert False, 'NotImplementedError expected' - -add_test('Create a line object - should fail', create_line_object) - -def print_line(): - chip = gpiod.Chip('gpio-mockup-A') - line = chip.get_line(3) - print(line) - chip.close() - -add_test('Print line object', print_line) - -def find_line(): - line = gpiod.find_line('gpio-mockup-A-4') - print('found line - offset: {}'.format(line.offset())) - line.owner().close() - -add_test('Find line globally', find_line) - -def create_empty_line_bulk(): - try: - lines = gpiod.LineBulk() - except TypeError: - print('Error as expected') - return - - assert False, 'TypeError expected' - -add_test('Create a line bulk object - should fail', create_empty_line_bulk) - -def get_lines(): - chip = gpiod.Chip('gpio-mockup-A') - - print('getting four lines from chip') - lines = chip.get_lines([2, 4, 5, 7]) - - print('Retrieved lines:') - for line in lines: - print(line) - - chip.close() - -add_test('Get lines from chip', get_lines) - -def get_all_lines(): - chip = gpiod.Chip('gpio-mockup-A') - - print('Retrieving all lines from chip') - lines = chip.get_all_lines() - - print('Retrieved lines:') - for line in lines: - print(line) - - chip.close() - -add_test('Get all lines from chip', get_all_lines) - -def find_lines(): - chip = gpiod.Chip('gpiochip0') - - print('looking up lines by names') - lines = chip.find_lines(['gpio-mockup-A-3', 'gpio-mockup-A-4', 'gpio-mockup-A-7']) - - print('Retrieved lines:') - for line in lines: - print(line) - - chip.close() - -add_test('Find multiple lines by name', find_lines) - -def find_lines_one_bad(): - chip = gpiod.Chip('gpiochip0') - - print('looking up lines by names') - try: - lines = chip.find_lines(['gpio-mockup-A-3', 'nonexistent', 'gpio-mockup-A-7']) - except TypeError as ex: - print('Error as expected') - return - - assert False, 'TypeError expected' - -add_test('Find multiple lines but one line name is non-existent', find_lines_one_bad) - -def create_line_bulk_from_lines(): - chip = gpiod.Chip('gpio-mockup-A') - line1 = chip.get_line(2) - line2 = chip.get_line(4) - line3 = chip.get_line(6) - lines = gpiod.LineBulk([line1, line2, line3]) - print('Created LineBulk:') - print(lines) - chip.close() - -add_test('Create a LineBulk from a list of lines', create_line_bulk_from_lines) - -def line_bulk_to_list(): - chip = gpiod.Chip('gpio-mockup-A') - lines = chip.get_lines((1, 2, 3)) - print(lines.to_list()) - chip.close() - -add_test('Convert a LineBulk to a list', line_bulk_to_list) - -def line_flags(): - chip = gpiod.Chip('gpiochip0') - line = chip.get_line(3) - - print('line is used: {}'.format(line.is_used())) - print('line is requested: {}'.format(line.is_requested())) - - print('requesting line') - line.request(consumer="gpiod_test.py", type=gpiod.LINE_REQ_DIR_OUT, - flags=(gpiod.LINE_REQ_FLAG_OPEN_DRAIN | gpiod.LINE_REQ_FLAG_ACTIVE_LOW)) - - print('line is used: {}'.format(line.is_used())) - print('line is open drain: {}'.format(line.is_open_drain())) - print('line is open source: {}'.format(line.is_open_source())) - print('line is requested: {}'.format(line.is_requested())) - print('line is active-low: {}'.format( - "True" if line.active_state() == gpiod.Line.ACTIVE_LOW else "False")) - - chip.close() - -add_test('Check various line flags', line_flags) - -def get_value_single_line(): - chip = gpiod.Chip('gpio-mockup-A') - line = chip.get_line(2) - line.request(consumer="gpiod_test.py", type=gpiod.LINE_REQ_DIR_IN) - print('line value: {}'.format(line.get_value())) - chip.close() - -add_test('Get value - single line', get_value_single_line) - -def set_value_single_line(): - chip = gpiod.Chip('gpiochip0') - line = chip.get_line(3) - line.request(consumer="gpiod_test.py", type=gpiod.LINE_REQ_DIR_IN) - - print('line value before: {}'.format(line.get_value())) - line.release() - line.request(consumer="gpiod_test.py", type=gpiod.LINE_REQ_DIR_OUT) - line.set_value(1) - line.release() - line.request(consumer="gpiod_test.py", type=gpiod.LINE_REQ_DIR_IN) - print('line value after: {}'.format(line.get_value())) - - chip.close() - -add_test('Set value - single line', set_value_single_line) - -def request_line_with_default_values(): - chip = gpiod.Chip('gpiochip0') - line = chip.get_line(3) - - print('requesting a single line with a default value') - line.request(consumer='gpiod_test.py', type=gpiod.LINE_REQ_DIR_OUT, default_vals=[ 1 ]) - - print('line value after request: {}'.format(line.get_value())) - - chip.close() - -add_test('Request line with default value', request_line_with_default_values) - -def request_multiple_lines_with_default_values(): - chip = gpiod.Chip('gpiochip0') - lines = chip.get_lines(( 1, 2, 3, 4, 5 )) - - print('requesting lines with default values') - lines.request(consumer='gpiod_test.py', type=gpiod.LINE_REQ_DIR_OUT, default_vals=( 1, 0, 1, 0, 1 )) - - print('line values after request: {}'.format(lines.get_values())) - - chip.close() - -add_test('Request multiple lines with default values', request_multiple_lines_with_default_values) - -def request_line_incorrect_number_of_def_vals(): - with gpiod.Chip('gpiochip0') as chip: - lines = chip.get_lines(( 1, 2, 3, 4, 5 )) - - print('requesting lines with incorrect number of default values') - try: - lines.request(consumer='gpiod_test.py', - type=gpiod.LINE_REQ_DIR_OUT, - default_vals=( 1, 0, 1, 0 )) - except TypeError: - print('TypeError raised as expected') - return - - assert False, 'TypeError expected' - -add_test('Request with incorrect number of default values', request_line_incorrect_number_of_def_vals) - -def line_event_single_line(): - chip = gpiod.Chip('gpiochip0') - line = chip.get_line(1) - - print('requesting line for events') - line.request(consumer="gpiod_test.py", type=gpiod.LINE_REQ_EV_BOTH_EDGES) - - print('generating a line event') - fire_line_event('gpiochip0', 1, True) - assert line.event_wait(sec=1), 'Expected a line event to occur' - - print('event received') - event = line.event_read() - print_event(event) - - chip.close() - -add_test('Monitor a single line for events', line_event_single_line) - -def line_event_multiple_lines(): - chip = gpiod.Chip('gpiochip0') - lines = chip.get_lines((1, 2, 3, 4, 5)) - - print('requesting lines for events') - lines.request(consumer="gpiod_test.py", type=gpiod.LINE_REQ_EV_BOTH_EDGES) - - print('generating two line events') - fire_line_event('gpiochip0', 1, True) - fire_line_event('gpiochip0', 2, True) - - events = lines.event_wait(sec=1) - assert events is not None and len(events) == 2, 'Expected to receive two line events' - - print('events received:') - for line in events: - event = line.event_read() - print_event(event) - - chip.close() - -add_test('Monitor multiple lines for events', line_event_multiple_lines) - -def line_event_poll_fd(): - chip = gpiod.Chip('gpiochip0') - lines = chip.get_lines((1, 2, 3, 4, 5, 6)) - print('requesting lines for events') - lines.request(consumer="gpiod_test.py", type=gpiod.LINE_REQ_EV_BOTH_EDGES) - - print('generating three line events') - fire_line_event('gpiochip0', 2, True) - fire_line_event('gpiochip0', 3, False) - fire_line_event('gpiochip0', 5, True) - - print('retrieving the file descriptors') - inputs = [] - fd_mapping = {} - for line in lines: - inputs.append(line.event_get_fd()) - fd_mapping[line.event_get_fd()] = line - - readable, writable, exceptional = select.select(inputs, [], inputs, 1.0) - assert len(readable) == 3, 'Expected to receive three line events' - - print('events received:') - for fd in readable: - line = fd_mapping[fd] - event = line.event_read() - print_event(event) - - chip.close() - -add_test('Monitor multiple lines using their file descriptors', line_event_poll_fd) - -def line_event_repr(): - with gpiod.Chip('gpiochip0') as chip: - line = chip.get_line(1) - - print('requesting line for events') - line.request(consumer="gpiod_test.py", type=gpiod.LINE_REQ_EV_BOTH_EDGES) - - print('generating a line event') - fire_line_event('gpiochip0', 1, True) - assert line.event_wait(sec=1), 'Expected a line event to occur' - - print('event received: {}'.format(line.event_read())) - -add_test('Line event string repr', line_event_repr) - -print('API version is {}'.format(gpiod.version_string())) - -for name, func in test_cases: - print('==============================================') - print('{}:'.format(name)) - print() - func() diff --git a/bindings/python/tests/Makefile.am b/bindings/python/tests/Makefile.am new file mode 100644 index 0000000..937c673 --- /dev/null +++ b/bindings/python/tests/Makefile.am @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# +# This file is part of libgpiod. +# +# Copyright (C) 2019 Bartosz Golaszewski +# + +bin_SCRIPTS = gpiod_py_test.py + +pyexec_LTLIBRARIES = gpiomockup.la + +gpiomockup_la_SOURCES = gpiomockupmodule.c +gpiomockup_la_CFLAGS = -I$(top_srcdir)/tests/mockup/ +gpiomockup_la_CFLAGS += -Wall -Wextra -g $(PYTHON_CPPFLAGS) +gpiomockup_la_LDFLAGS = -module -avoid-version +gpiomockup_la_LIBADD = $(top_builddir)/tests/mockup/libgpiomockup.la +gpiomockup_la_LIBADD += $(PYTHON_LIBS) diff --git a/bindings/python/tests/gpiod_py_test.py b/bindings/python/tests/gpiod_py_test.py new file mode 100755 index 0000000..ede1ae9 --- /dev/null +++ b/bindings/python/tests/gpiod_py_test.py @@ -0,0 +1,650 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: LGPL-2.1-or-later + +# +# This file is part of libgpiod. +# +# Copyright (C) 2019 Bartosz Golaszewski +# + +import errno +import gpiod +import gpiomockup +import os +import select +import threading +import unittest + +from packaging import version + +mockup = None +default_consumer = 'gpiod-py-test' + +class MockupTestCase(unittest.TestCase): + + chip_sizes = None + flags = 0 + + def setUp(self): + mockup.probe(self.chip_sizes, flags=self.flags) + + def tearDown(self): + mockup.remove() + +class EventThread(threading.Thread): + + def __init__(self, chip_idx, line_offset, freq): + threading.Thread.__init__(self) + self.chip_idx = chip_idx + self.line_offset = line_offset + self.freq = freq + self.lock = threading.Lock() + self.cond = threading.Condition(self.lock) + self.should_stop = False + + def run(self): + i = 0; + while True: + with self.lock: + if self.should_stop: + break; + + if not self.cond.wait(float(self.freq) / 1000): + mockup.chip_set_pull(self.chip_idx, + self.line_offset, i % 2) + i += 1 + + def stop(self): + with self.lock: + self.should_stop = True + self.cond.notify_all() + + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.stop() + self.join() + +def check_kernel(major, minor, release): + current = os.uname().release + required = '{}.{}.{}'.format(major, minor, release) + if version.parse(current) < version.parse(required): + raise NotImplementedError( + 'linux kernel version must be at least {} - got {}'.format(required, current)) + +# +# Chip test cases +# + +class ChipOpen(MockupTestCase): + + chip_sizes = ( 8, 8, 8 ) + + def test_open_chip_by_name(self): + with gpiod.Chip(mockup.chip_name(1), gpiod.Chip.OPEN_BY_NAME) as chip: + self.assertEqual(chip.name(), mockup.chip_name(1)) + + def test_open_chip_by_path(self): + with gpiod.Chip(mockup.chip_path(1), gpiod.Chip.OPEN_BY_PATH) as chip: + self.assertEqual(chip.name(), mockup.chip_name(1)) + + def test_open_chip_by_num(self): + with gpiod.Chip('{}'.format(mockup.chip_num(1)), + gpiod.Chip.OPEN_BY_NUMBER) as chip: + self.assertEqual(chip.name(), mockup.chip_name(1)) + + def test_open_chip_by_label(self): + with gpiod.Chip('gpio-mockup-B', gpiod.Chip.OPEN_BY_LABEL) as chip: + self.assertEqual(chip.name(), mockup.chip_name(1)) + + def test_lookup_chip_by_name(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + self.assertEqual(chip.name(), mockup.chip_name(1)) + + def test_lookup_chip_by_path(self): + with gpiod.Chip(mockup.chip_path(1)) as chip: + self.assertEqual(chip.name(), mockup.chip_name(1)) + + def test_lookup_chip_by_num(self): + with gpiod.Chip('{}'.format(mockup.chip_num(1))) as chip: + self.assertEqual(chip.name(), mockup.chip_name(1)) + + def test_lookup_chip_by_label(self): + with gpiod.Chip('gpio-mockup-B') as chip: + self.assertEqual(chip.name(), mockup.chip_name(1)) + + def test_nonexistent_chip(self): + with self.assertRaises(FileNotFoundError): + chip = gpiod.Chip('nonexistent-chip') + + def test_open_chip_no_arguments(self): + with self.assertRaises(TypeError): + chip = gpiod.Chip() + +class ChipClose(MockupTestCase): + + chip_sizes = ( 8, ) + + def test_use_chip_after_close(self): + chip = gpiod.Chip(mockup.chip_name(0)) + self.assertEqual(chip.name(), mockup.chip_name(0)) + chip.close() + with self.assertRaises(ValueError): + chip.name() + +class ChipInfo(MockupTestCase): + + chip_sizes = ( 16, ) + + def test_chip_get_info(self): + chip = gpiod.Chip(mockup.chip_name(0)) + self.assertEqual(chip.name(), mockup.chip_name(0)) + self.assertEqual(chip.label(), 'gpio-mockup-A') + self.assertEqual(chip.num_lines(), 16) + +class ChipGetLines(MockupTestCase): + + chip_sizes = ( 8, 8, 4 ) + flags = gpiomockup.Mockup.FLAG_NAMED_LINES + + def test_get_single_line_by_offset(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + line = chip.get_line(4) + self.assertEqual(line.name(), 'gpio-mockup-B-4') + + def test_find_single_line_by_name(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + line = chip.find_line('gpio-mockup-B-4') + self.assertEqual(line.offset(), 4) + + def test_get_single_line_invalid_offset(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + with self.assertRaises(OSError) as err_ctx: + line = chip.get_line(11) + + self.assertEqual(err_ctx.exception.errno, errno.EINVAL) + + def test_find_single_line_nonexistent(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + line = chip.find_line('nonexistent-line') + self.assertEqual(line, None) + + def test_get_multiple_lines_by_offsets_in_tuple(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + lines = chip.get_lines(( 1, 3, 6, 7 )).to_list() + self.assertEqual(len(lines), 4) + self.assertEqual(lines[0].name(), 'gpio-mockup-B-1') + self.assertEqual(lines[1].name(), 'gpio-mockup-B-3') + self.assertEqual(lines[2].name(), 'gpio-mockup-B-6') + self.assertEqual(lines[3].name(), 'gpio-mockup-B-7') + + def test_get_multiple_lines_by_offsets_in_list(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + lines = chip.get_lines([ 1, 3, 6, 7 ]).to_list() + self.assertEqual(len(lines), 4) + self.assertEqual(lines[0].name(), 'gpio-mockup-B-1') + self.assertEqual(lines[1].name(), 'gpio-mockup-B-3') + self.assertEqual(lines[2].name(), 'gpio-mockup-B-6') + self.assertEqual(lines[3].name(), 'gpio-mockup-B-7') + + def test_find_multiple_lines_by_names_in_tuple(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + lines = chip.find_lines(( 'gpio-mockup-B-0', + 'gpio-mockup-B-3', + 'gpio-mockup-B-4', + 'gpio-mockup-B-6' )).to_list() + self.assertEqual(len(lines), 4) + self.assertEqual(lines[0].offset(), 0) + self.assertEqual(lines[1].offset(), 3) + self.assertEqual(lines[2].offset(), 4) + self.assertEqual(lines[3].offset(), 6) + + def test_find_multiple_lines_by_names_in_list(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + lines = chip.find_lines([ 'gpio-mockup-B-0', + 'gpio-mockup-B-3', + 'gpio-mockup-B-4', + 'gpio-mockup-B-6' ]).to_list() + self.assertEqual(len(lines), 4) + self.assertEqual(lines[0].offset(), 0) + self.assertEqual(lines[1].offset(), 3) + self.assertEqual(lines[2].offset(), 4) + self.assertEqual(lines[3].offset(), 6) + + def test_get_multiple_lines_invalid_offset(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + with self.assertRaises(OSError) as err_ctx: + line = chip.get_lines(( 1, 3, 11, 7 )) + + self.assertEqual(err_ctx.exception.errno, errno.EINVAL) + + def test_find_multiple_lines_nonexistent(self): + with gpiod.Chip(mockup.chip_name(1)) as chip: + with self.assertRaises(TypeError): + lines = chip.find_lines(( 'gpio-mockup-B-0', + 'nonexistent-line', + 'gpio-mockup-B-4', + 'gpio-mockup-B-6' )).to_list() + + def test_get_all_lines(self): + with gpiod.Chip(mockup.chip_name(2)) as chip: + lines = chip.get_all_lines().to_list() + self.assertEqual(len(lines), 4) + self.assertEqual(lines[0].name(), 'gpio-mockup-C-0') + self.assertEqual(lines[1].name(), 'gpio-mockup-C-1') + self.assertEqual(lines[2].name(), 'gpio-mockup-C-2') + self.assertEqual(lines[3].name(), 'gpio-mockup-C-3') + +# +# Line test cases +# + +class LineGlobalFindLine(MockupTestCase): + + chip_sizes = ( 4, 8, 16 ) + flags = gpiomockup.Mockup.FLAG_NAMED_LINES + + def test_global_find_line_function(self): + line = gpiod.find_line('gpio-mockup-B-4') + self.assertNotEqual(line, None) + try: + self.assertEqual(line.owner().label(), 'gpio-mockup-B') + self.assertEqual(line.offset(), 4) + finally: + line.owner().close() + + def test_global_find_line_function_nonexistent(self): + line = gpiod.find_line('nonexistent-line') + self.assertEqual(line, None) + +class LineInfo(MockupTestCase): + + chip_sizes = ( 8, ) + flags = gpiomockup.Mockup.FLAG_NAMED_LINES + + def test_unexported_line(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(4) + self.assertEqual(line.offset(), 4) + self.assertEqual(line.name(), 'gpio-mockup-A-4') + self.assertEqual(line.direction(), gpiod.Line.DIRECTION_INPUT) + self.assertEqual(line.active_state(), gpiod.Line.ACTIVE_HIGH) + self.assertEqual(line.consumer(), None) + self.assertFalse(line.is_used()) + self.assertFalse(line.is_requested()) + + def test_exported_line(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(4) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_OUT, + flags=gpiod.LINE_REQ_FLAG_ACTIVE_LOW) + self.assertEqual(line.offset(), 4) + self.assertEqual(line.name(), 'gpio-mockup-A-4') + self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT) + self.assertEqual(line.active_state(), gpiod.Line.ACTIVE_LOW) + self.assertEqual(line.consumer(), default_consumer) + self.assertTrue(line.is_used()) + self.assertTrue(line.is_requested()) + + def test_exported_line_with_flags(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(4) + flags = (gpiod.LINE_REQ_FLAG_ACTIVE_LOW | + gpiod.LINE_REQ_FLAG_OPEN_DRAIN) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_OUT, + flags=flags) + self.assertEqual(line.offset(), 4) + self.assertEqual(line.name(), 'gpio-mockup-A-4') + # FIXME Uncomment the line below once this issue is fixed in the kernel. + #self.assertEqual(line.direction(), gpiod.Line.DIRECTION_OUTPUT) + self.assertEqual(line.active_state(), gpiod.Line.ACTIVE_LOW) + self.assertEqual(line.consumer(), default_consumer) + self.assertTrue(line.is_used()) + self.assertTrue(line.is_requested()) + self.assertTrue(line.is_open_drain()) + self.assertFalse(line.is_open_source()) + +class LineValues(MockupTestCase): + + chip_sizes = ( 8, ) + + def test_get_value_single_line(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_IN) + self.assertEqual(line.get_value(), 0) + mockup.chip_set_pull(0, 3, 1) + self.assertEqual(line.get_value(), 1) + + def test_set_value_single_line(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_OUT) + line.set_value(1) + self.assertEqual(mockup.chip_get_value(0, 3), 1) + line.set_value(0) + self.assertEqual(mockup.chip_get_value(0, 3), 0) + + def test_set_value_with_default_value_argument(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_OUT, + default_val=1) + self.assertEqual(mockup.chip_get_value(0, 3), 1) + + def test_get_value_multiple_lines(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + lines = chip.get_lines(( 0, 3, 4, 6 )) + lines.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_IN) + self.assertEqual(lines.get_values(), [ 0, 0, 0, 0 ]) + mockup.chip_set_pull(0, 0, 1) + mockup.chip_set_pull(0, 4, 1) + mockup.chip_set_pull(0, 6, 1) + self.assertEqual(lines.get_values(), [ 1, 0, 1, 1 ]) + + def test_set_value_multiple_lines(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + lines = chip.get_lines(( 0, 3, 4, 6 )) + lines.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_OUT) + lines.set_values(( 1, 0, 1, 1 )) + self.assertEqual(mockup.chip_get_value(0, 0), 1) + self.assertEqual(mockup.chip_get_value(0, 3), 0) + self.assertEqual(mockup.chip_get_value(0, 4), 1) + self.assertEqual(mockup.chip_get_value(0, 6), 1) + lines.set_values(( 0, 0, 1, 0 )) + self.assertEqual(mockup.chip_get_value(0, 0), 0) + self.assertEqual(mockup.chip_get_value(0, 3), 0) + self.assertEqual(mockup.chip_get_value(0, 4), 1) + self.assertEqual(mockup.chip_get_value(0, 6), 0) + + def test_set_multiple_values_with_default_vals_argument(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + lines = chip.get_lines(( 0, 3, 4, 6 )) + lines.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_OUT, + default_vals=( 1, 0, 1, 1 )) + self.assertEqual(mockup.chip_get_value(0, 0), 1) + self.assertEqual(mockup.chip_get_value(0, 3), 0) + self.assertEqual(mockup.chip_get_value(0, 4), 1) + self.assertEqual(mockup.chip_get_value(0, 6), 1) + + def test_get_value_active_low(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_IN, + flags=gpiod.LINE_REQ_FLAG_ACTIVE_LOW) + self.assertEqual(line.get_value(), 1) + mockup.chip_set_pull(0, 3, 1) + self.assertEqual(line.get_value(), 0) + + def test_set_value_active_low(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_OUT, + flags=gpiod.LINE_REQ_FLAG_ACTIVE_LOW) + line.set_value(1) + self.assertEqual(mockup.chip_get_value(0, 3), 0) + line.set_value(0) + self.assertEqual(mockup.chip_get_value(0, 3), 1) + +class LineRequestBehavior(MockupTestCase): + + chip_sizes = ( 8, ) + + def test_line_export_release(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_IN) + self.assertTrue(line.is_requested()) + self.assertEqual(line.get_value(), 0) + line.release() + self.assertFalse(line.is_requested()) + + def test_line_request_twice_two_calls(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_IN) + with self.assertRaises(OSError): + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_IN) + + def test_line_request_twice_in_bulk(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + lines = chip.get_lines(( 2, 3, 6, 6 )) + with self.assertRaises(OSError): + lines.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_IN) + + def test_use_value_unrequested(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + with self.assertRaises(OSError): + line.get_value() + +# +# Iterator test cases +# + +class ChipIterator(MockupTestCase): + + chip_sizes = ( 4, 8, 16 ) + + def test_iterate_over_chips(self): + gotA = False + gotB = False + gotC = False + + for chip in gpiod.ChipIter(): + if chip.label() == 'gpio-mockup-A': + gotA = True + elif chip.label() == 'gpio-mockup-B': + gotB = True + elif chip.label() == 'gpio-mockup-C': + gotC = True + + self.assertTrue(gotA) + self.assertTrue(gotB) + self.assertTrue(gotC) + +class LineIterator(MockupTestCase): + + chip_sizes = ( 4, ) + + def test_iterate_over_lines(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + count = 0 + + for line in gpiod.LineIter(chip): + self.assertEqual(line.offset(), count) + count += 1 + + self.assertEqual(count, chip.num_lines()) + +class LineBulkIter(MockupTestCase): + + chip_sizes = ( 4, ) + + def test_line_bulk_iterator(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + lines = chip.get_all_lines() + count = 0 + + for line in lines: + self.assertEqual(line.offset(), count) + count += 1 + + self.assertEqual(count, chip.num_lines()) + +# +# Event test cases +# + +class EventSingleLine(MockupTestCase): + + chip_sizes = ( 8, ) + + def test_single_line_rising_edge_event(self): + with EventThread(0, 4, 200): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(4) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_EV_RISING_EDGE) + self.assertTrue(line.event_wait(sec=1)) + event = line.event_read() + self.assertEqual(event.type, gpiod.LineEvent.RISING_EDGE) + self.assertEqual(event.source.offset(), 4) + + def test_single_line_falling_edge_event(self): + with EventThread(0, 4, 200): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(4) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_EV_FALLING_EDGE) + self.assertTrue(line.event_wait(sec=1)) + event = line.event_read() + self.assertEqual(event.type, gpiod.LineEvent.FALLING_EDGE) + self.assertEqual(event.source.offset(), 4) + + def test_single_line_both_edges_events(self): + with EventThread(0, 4, 200): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(4) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_EV_BOTH_EDGES) + self.assertTrue(line.event_wait(sec=1)) + event = line.event_read() + self.assertEqual(event.type, gpiod.LineEvent.RISING_EDGE) + self.assertEqual(event.source.offset(), 4) + self.assertTrue(line.event_wait(sec=1)) + event = line.event_read() + self.assertEqual(event.type, gpiod.LineEvent.FALLING_EDGE) + self.assertEqual(event.source.offset(), 4) + + def test_single_line_both_edges_events_active_low(self): + with EventThread(0, 4, 200): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(4) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_EV_BOTH_EDGES, + flags=gpiod.LINE_REQ_FLAG_ACTIVE_LOW) + self.assertTrue(line.event_wait(sec=1)) + event = line.event_read() + self.assertEqual(event.type, gpiod.LineEvent.FALLING_EDGE) + self.assertEqual(event.source.offset(), 4) + self.assertTrue(line.event_wait(sec=1)) + event = line.event_read() + self.assertEqual(event.type, gpiod.LineEvent.RISING_EDGE) + self.assertEqual(event.source.offset(), 4) + +class EventBulk(MockupTestCase): + + chip_sizes = ( 8, ) + + def test_watch_multiple_lines_for_events(self): + with EventThread(0, 2, 200): + with gpiod.Chip(mockup.chip_name(0)) as chip: + lines = chip.get_lines(( 0, 1, 2, 3, 4 )) + lines.request(consumer=default_consumer, + type=gpiod.LINE_REQ_EV_BOTH_EDGES) + event_lines = lines.event_wait(sec=1) + self.assertEqual(len(event_lines), 1) + line = event_lines[0] + event = line.event_read() + self.assertEqual(event.type, gpiod.LineEvent.RISING_EDGE) + self.assertEqual(event.source.offset(), 2) + event_lines = lines.event_wait(sec=1) + self.assertEqual(len(event_lines), 1) + line = event_lines[0] + event = line.event_read() + self.assertEqual(event.type, gpiod.LineEvent.FALLING_EDGE) + self.assertEqual(event.source.offset(), 2) + +class EventValues(MockupTestCase): + + chip_sizes = ( 8, ) + + def test_request_for_events_get_value(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_EV_BOTH_EDGES) + self.assertEqual(line.get_value(), 0) + mockup.chip_set_pull(0, 3, 1) + self.assertEqual(line.get_value(), 1) + + def test_request_for_events_get_value_active_low(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_EV_BOTH_EDGES, + flags=gpiod.LINE_REQ_FLAG_ACTIVE_LOW) + self.assertEqual(line.get_value(), 1) + mockup.chip_set_pull(0, 3, 1) + self.assertEqual(line.get_value(), 0) + +class EventFileDescriptor(MockupTestCase): + + chip_sizes = ( 8, ) + + def test_event_get_fd(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_EV_BOTH_EDGES) + fd = line.event_get_fd(); + self.assertGreaterEqual(fd, 0) + + def test_event_get_fd_not_requested(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + with self.assertRaises(OSError): + fd = line.event_get_fd(); + + def test_event_get_fd_requested_for_values(self): + with gpiod.Chip(mockup.chip_name(0)) as chip: + line = chip.get_line(3) + line.request(consumer=default_consumer, + type=gpiod.LINE_REQ_DIR_IN) + with self.assertRaises(OSError): + fd = line.event_get_fd(); + + def test_event_fd_polling(self): + with EventThread(0, 2, 200): + with gpiod.Chip(mockup.chip_name(0)) as chip: + lines = chip.get_lines(( 0, 1, 2, 3, 4, 5, 6 )) + lines.request(consumer=default_consumer, + type=gpiod.LINE_REQ_EV_BOTH_EDGES) + + inputs = [] + for line in lines: + inputs.append(line.event_get_fd()) + + readable, writable, exceptional = select.select(inputs, [], + inputs, 1.0) + + self.assertEqual(len(readable), 1) + event = lines.to_list()[2].event_read() + self.assertEqual(event.type, gpiod.LineEvent.RISING_EDGE) + self.assertEqual(event.source.offset(), 2) + +# +# Main +# + +if __name__ == '__main__': + check_kernel(5, 2, 7) + mockup = gpiomockup.Mockup() + unittest.main() diff --git a/bindings/python/tests/gpiomockupmodule.c b/bindings/python/tests/gpiomockupmodule.c new file mode 100644 index 0000000..d54ecf9 --- /dev/null +++ b/bindings/python/tests/gpiomockupmodule.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * This file is part of libgpiod. + * + * Copyright (C) 2019 Bartosz Golaszewski + */ + +#include +#include + +typedef struct { + PyObject_HEAD + struct gpio_mockup *mockup; +} gpiomockup_MockupObject; + +enum { + gpiomockup_FLAG_NAMED_LINES = 1, +}; + +static int gpiomockup_Mockup_init(gpiomockup_MockupObject *self, + PyObject *Py_UNUSED(ignored0), + PyObject *Py_UNUSED(ignored1)) +{ + Py_BEGIN_ALLOW_THREADS; + self->mockup = gpio_mockup_new(); + Py_END_ALLOW_THREADS; + if (!self->mockup) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + return 0; +} + +static void gpiomockup_Mockup_dealloc(gpiomockup_MockupObject *self) +{ + if (self->mockup) { + Py_BEGIN_ALLOW_THREADS; + gpio_mockup_unref(self->mockup); + Py_END_ALLOW_THREADS; + } + + PyObject_Del(self); +} + +static PyObject *gpiomockup_Mockup_probe(gpiomockup_MockupObject *self, + PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = { "chip_sizes", + "flags", + NULL }; + + PyObject *chip_sizes_obj, *iter, *next; + unsigned int *chip_sizes; + int ret, flags = 0, i; + Py_ssize_t num_chips; + + ret = PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, + &chip_sizes_obj, &flags); + if (!ret) + return NULL; + + num_chips = PyObject_Size(chip_sizes_obj); + if (num_chips < 0) { + return NULL; + } else if (num_chips == 0) { + PyErr_SetString(PyExc_TypeError, + "Number of chips must be greater thatn 0"); + return NULL; + } + + chip_sizes = PyMem_RawCalloc(num_chips, sizeof(unsigned int)); + if (!chip_sizes) + return NULL; + + iter = PyObject_GetIter(chip_sizes_obj); + if (!iter) { + PyMem_RawFree(chip_sizes); + return NULL; + } + + for (i = 0;; i++) { + next = PyIter_Next(iter); + if (!next) { + Py_DECREF(iter); + break; + } + + chip_sizes[i] = PyLong_AsUnsignedLong(next); + Py_DECREF(next); + if (PyErr_Occurred()) { + Py_DECREF(iter); + PyMem_RawFree(chip_sizes); + return NULL; + } + } + + if (flags & gpiomockup_FLAG_NAMED_LINES) + flags |= GPIO_MOCKUP_FLAG_NAMED_LINES; + + Py_BEGIN_ALLOW_THREADS; + ret = gpio_mockup_probe(self->mockup, num_chips, chip_sizes, flags); + Py_END_ALLOW_THREADS; + PyMem_RawFree(chip_sizes); + if (ret) + return NULL; + + Py_RETURN_NONE; +} + +static PyObject *gpiomockup_Mockup_remove(gpiomockup_MockupObject *self, + PyObject *Py_UNUSED(ignored)) +{ + int ret; + + Py_BEGIN_ALLOW_THREADS; + ret = gpio_mockup_remove(self->mockup); + Py_END_ALLOW_THREADS; + if (ret) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject *gpiomockup_Mockup_chip_name(gpiomockup_MockupObject *self, + PyObject *args) +{ + unsigned int idx; + const char *name; + int ret; + + ret = PyArg_ParseTuple(args, "I", &idx); + if (!ret) + return NULL; + + name = gpio_mockup_chip_name(self->mockup, idx); + if (!name) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + return PyUnicode_FromString(name); +} + +static PyObject *gpiomockup_Mockup_chip_path(gpiomockup_MockupObject *self, + PyObject *args) +{ + unsigned int idx; + const char *path; + int ret; + + ret = PyArg_ParseTuple(args, "I", &idx); + if (!ret) + return NULL; + + path = gpio_mockup_chip_path(self->mockup, idx); + if (!path) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + return PyUnicode_FromString(path); +} + +static PyObject *gpiomockup_Mockup_chip_num(gpiomockup_MockupObject *self, + PyObject *args) +{ + unsigned int idx; + int ret, num; + + ret = PyArg_ParseTuple(args, "I", &idx); + if (!ret) + return NULL; + + num = gpio_mockup_chip_num(self->mockup, idx); + if (num < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + return PyLong_FromLong(num); +} + +static PyObject *gpiomockup_Mockup_chip_get_value(gpiomockup_MockupObject *self, + PyObject *args) +{ + unsigned int chip_idx, line_offset; + int ret, val; + + ret = PyArg_ParseTuple(args, "II", &chip_idx, &line_offset); + if (!ret) + return NULL; + + Py_BEGIN_ALLOW_THREADS; + val = gpio_mockup_get_value(self->mockup, chip_idx, line_offset); + Py_END_ALLOW_THREADS; + if (val < 0) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + return PyLong_FromUnsignedLong(val); +} + +static PyObject *gpiomockup_Mockup_chip_set_pull(gpiomockup_MockupObject *self, + PyObject *args) +{ + unsigned int chip_idx, line_offset; + int ret, pull; + + ret = PyArg_ParseTuple(args, "IIi", &chip_idx, &line_offset, &pull); + if (!ret) + return NULL; + + Py_BEGIN_ALLOW_THREADS; + ret = gpio_mockup_set_pull(self->mockup, chip_idx, line_offset, pull); + Py_END_ALLOW_THREADS; + if (ret) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyMethodDef gpiomockup_Mockup_methods[] = { + { + .ml_name = "probe", + .ml_meth = (PyCFunction)(void (*)(void))gpiomockup_Mockup_probe, + .ml_flags = METH_VARARGS | METH_KEYWORDS, + }, + { + .ml_name = "remove", + .ml_meth = (PyCFunction)gpiomockup_Mockup_remove, + .ml_flags = METH_NOARGS, + }, + { + .ml_name = "chip_name", + .ml_meth = (PyCFunction)gpiomockup_Mockup_chip_name, + .ml_flags = METH_VARARGS, + }, + { + .ml_name = "chip_path", + .ml_meth = (PyCFunction)gpiomockup_Mockup_chip_path, + .ml_flags = METH_VARARGS, + }, + { + .ml_name = "chip_num", + .ml_meth = (PyCFunction)gpiomockup_Mockup_chip_num, + .ml_flags = METH_VARARGS, + }, + { + .ml_name = "chip_get_value", + .ml_meth = (PyCFunction)gpiomockup_Mockup_chip_get_value, + .ml_flags = METH_VARARGS, + }, + { + .ml_name = "chip_set_pull", + .ml_meth = (PyCFunction)gpiomockup_Mockup_chip_set_pull, + .ml_flags = METH_VARARGS, + }, + { } +}; + +static PyTypeObject gpiomockup_MockupType = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "gpiomockup.Mockup", + .tp_basicsize = sizeof(gpiomockup_MockupObject), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_new = PyType_GenericNew, + .tp_init = (initproc)gpiomockup_Mockup_init, + .tp_dealloc = (destructor)gpiomockup_Mockup_dealloc, + .tp_methods = gpiomockup_Mockup_methods, +}; + +static PyModuleDef gpiomockup_Module = { + PyModuleDef_HEAD_INIT, + .m_name = "gpiomockup", + .m_size = -1, +}; + +PyMODINIT_FUNC PyInit_gpiomockup(void) +{ + PyObject *module, *val; + int ret; + + module = PyModule_Create(&gpiomockup_Module); + if (!module) + return NULL; + + ret = PyType_Ready(&gpiomockup_MockupType); + if (ret) + return NULL; + Py_INCREF(&gpiomockup_MockupType); + + ret = PyModule_AddObject(module, "Mockup", + (PyObject *)&gpiomockup_MockupType); + if (ret) + return NULL; + + val = PyLong_FromLong(gpiomockup_FLAG_NAMED_LINES); + if (!val) + return NULL; + + ret = PyDict_SetItemString(gpiomockup_MockupType.tp_dict, + "FLAG_NAMED_LINES", val); + if (ret) + return NULL; + + return module; +} diff --git a/configure.ac b/configure.ac index bf364e7..1978eea 100644 --- a/configure.ac +++ b/configure.ac @@ -220,6 +220,7 @@ AC_CONFIG_FILES([libgpiod.pc bindings/cxx/tests/Makefile bindings/python/Makefile bindings/python/examples/Makefile + bindings/python/tests/Makefile man/Makefile]) AC_OUTPUT -- 2.30.2