From: Bartosz Golaszewski Date: Wed, 2 May 2018 10:19:39 +0000 (+0200) Subject: bindings: cxx: new test cases X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=58a17c3858c7eb99a661a6bf36681c50e6f64af6;p=qemu-gpiodev%2Flibgpiod.git bindings: cxx: new test cases Implement more test cases for C++ bindings and extend the existing ones. While we're at it: use auto where applicable and log to stderr instead of stdout. Signed-off-by: Bartosz Golaszewski --- diff --git a/bindings/cxx/examples/gpiod_cxx_tests.cpp b/bindings/cxx/examples/gpiod_cxx_tests.cpp index 197b3e8..4c6e859 100644 --- a/bindings/cxx/examples/gpiod_cxx_tests.cpp +++ b/bindings/cxx/examples/gpiod_cxx_tests.cpp @@ -17,6 +17,12 @@ #include #include #include +#include +#include +#include +#include + +#include namespace { @@ -36,15 +42,49 @@ struct test_case test_func _test_func_##_func(_func); \ test_case _test_case_##_func(#_func, _test_func_##_func) -void chip_info(void) +std::string boolstr(bool b) { - ::gpiod::chip chip("gpiochip0"); + return b ? "true" : "false"; +} + +void fire_line_event(const ::std::string& chip, unsigned int offset, bool rising) +{ + ::std::string path; + + path = "/sys/kernel/debug/gpio-mockup-event/" + chip + "/" + ::std::to_string(offset); - ::std::cout << "chip name: " << chip.name() << ::std::endl; - ::std::cout << "chip label: " << chip.label() << ::std::endl; - ::std::cout << "number of lines " << chip.num_lines() << ::std::endl; + ::std::ofstream out(path); + if (!out) + throw ::std::runtime_error("unable to open " + path); + + out << ::std::to_string(rising ? 1 : 0) << ::std::endl; } -TEST_CASE(chip_info); + +void print_event(const ::gpiod::line_event& event) +{ + ::std::cerr << "type: " + << (event.event_type == ::gpiod::line_event::RISING_EDGE ? "rising" : "falling") + << "\n" + << "timestamp: " + << ::std::chrono::duration_cast<::std::chrono::seconds>(event.timestamp).count() + << "." + << event.timestamp.count() % 1000000000 + << "\n" + << "source line offset: " + << event.source.offset() + << ::std::endl; +} + +void chip_open_default_lookup(void) +{ + ::gpiod::chip by_name("gpiochip0"); + ::gpiod::chip by_path("/dev/gpiochip0"); + ::gpiod::chip by_label("gpio-mockup-A"); + ::gpiod::chip by_number("0"); + + ::std::cerr << "all good" << ::std::endl; +} +TEST_CASE(chip_open_default_lookup); void chip_open_different_modes(void) { @@ -52,30 +92,96 @@ void chip_open_different_modes(void) ::gpiod::chip by_path("/dev/gpiochip0", ::gpiod::chip::OPEN_BY_PATH); ::gpiod::chip by_label("gpio-mockup-A", ::gpiod::chip::OPEN_BY_LABEL); ::gpiod::chip by_number("0", ::gpiod::chip::OPEN_BY_NUMBER); + + ::std::cerr << "all good" << ::std::endl; +} +TEST_CASE(chip_open_different_modes); + +void chip_open_nonexistent(void) +{ + try { + ::gpiod::chip chip("/nonexistent_gpiochip"); + } catch (const ::std::system_error& exc) { + ::std::cerr << "system_error thrown as expected: " << exc.what() << ::std::endl; + return; + } + + throw ::std::runtime_error("system_error should have been thrown"); } -TEST_CASE(chip_open_different_modes); +TEST_CASE(chip_open_nonexistent); + +void chip_open_method(void) +{ + ::gpiod::chip chip; + + if (chip) + throw ::std::runtime_error("chip should be empty"); + + chip.open("gpiochip0"); + + ::std::cerr << "all good" << ::std::endl; +} +TEST_CASE(chip_open_method); + +void chip_reset(void) +{ + ::gpiod::chip chip("gpiochip0"); + + if (!chip) + throw ::std::runtime_error("chip object is not valid"); + + chip.reset(); + + if (chip) + throw ::std::runtime_error("chip should be invalid"); + + ::std::cerr << "all good" << ::std::endl; +} +TEST_CASE(chip_reset); + +void chip_info(void) +{ + ::gpiod::chip chip("gpiochip0"); + + ::std::cerr << "chip name: " << chip.name() << ::std::endl; + ::std::cerr << "chip label: " << chip.label() << ::std::endl; + ::std::cerr << "number of lines " << chip.num_lines() << ::std::endl; +} +TEST_CASE(chip_info); + +void chip_operators(void) +{ + ::gpiod::chip chip1("gpiochip0"); + auto chip2 = chip1; + + if ((chip1 != chip2) || !(chip1 == chip2)) + throw ::std::runtime_error("chip objects should be equal"); + + ::std::cerr << "all good" << ::std::endl; +} +TEST_CASE(chip_operators); void chip_line_ops(void) { ::gpiod::chip chip("gpiochip0"); - ::gpiod::line line = chip.get_line(3); - ::std::cout << "Got line by offset: " << line.offset() << ::std::endl; + auto line = chip.get_line(3); + ::std::cerr << "Got line by offset: " << line.offset() << ::std::endl; line = chip.find_line("gpio-mockup-A-4"); - ::std::cout << "Got line by name: " << line.name() << ::std::endl; + ::std::cerr << "Got line by name: " << line.name() << ::std::endl; - ::gpiod::line_bulk lines = chip.get_lines({ 1, 2, 3, 6, 6 }); - ::std::cout << "Got multiple lines by offset: "; + auto lines = chip.get_lines({ 1, 2, 3, 6, 6 }); + ::std::cerr << "Got multiple lines by offset: "; for (auto& it: lines) - ::std::cout << it.offset() << " "; - ::std::cout << ::std::endl; + ::std::cerr << it.offset() << " "; + ::std::cerr << ::std::endl; lines = chip.find_lines({ "gpio-mockup-A-1", "gpio-mockup-A-4", "gpio-mockup-A-7" }); - ::std::cout << "Got multiple lines by name: "; + ::std::cerr << "Got multiple lines by name: "; for (auto& it: lines) - ::std::cout << it.name() << " "; - ::std::cout << ::std::endl; + ::std::cerr << it.name() << " "; + ::std::cerr << ::std::endl; } TEST_CASE(chip_line_ops); @@ -84,17 +190,20 @@ void line_info(void) ::gpiod::chip chip("gpiochip0"); ::gpiod::line line = chip.get_line(2); - ::std::cout << "line offset: " << line.offset() << ::std::endl; - ::std::cout << "line name: " << line.name() << ::std::endl; - ::std::cout << "line direction: " + ::std::cerr << "line offset: " << line.offset() << ::std::endl; + ::std::cerr << "line name: " << line.name() << ::std::endl; + ::std::cerr << "line direction: " << (line.direction() == ::gpiod::line::DIRECTION_INPUT ? "input" : "output") << ::std::endl; + ::std::cerr << "line active state: " + << (line.active_state() == ::gpiod::line::ACTIVE_LOW ? + "active low" : "active high") << :: std::endl; } TEST_CASE(line_info); void empty_objects(void) { - ::std::cout << "Are initialized line & chip objects 'false'?" << ::std::endl; + ::std::cerr << "Are initialized line & chip objects 'false'?" << ::std::endl; ::gpiod::line line; if (line) @@ -104,21 +213,21 @@ void empty_objects(void) if (chip) throw ::std::logic_error("chip built with a default constructor should be 'false'"); - ::std::cout << "YES" << ::std::endl; + ::std::cerr << "YES" << ::std::endl; } TEST_CASE(empty_objects); void line_bulk_iterator(void) { - ::std::cout << "Checking line_bulk iterators" << ::std::endl; + ::std::cerr << "Checking line_bulk iterators" << ::std::endl; ::gpiod::chip chip("gpiochip0"); ::gpiod::line_bulk bulk = chip.get_lines({ 0, 1, 2, 3, 4 }); for (auto& it: bulk) - ::std::cout << it.name() << ::std::endl; + ::std::cerr << it.name() << ::std::endl; - ::std::cout << "DONE" << ::std::endl; + ::std::cerr << "DONE" << ::std::endl; } TEST_CASE(line_bulk_iterator); @@ -126,37 +235,64 @@ void single_line_test(void) { const ::std::string line_name("gpio-mockup-A-4"); - ::std::cout << "Looking up a GPIO line by name (" << line_name << ")" << ::std::endl; + ::std::cerr << "Looking up a GPIO line by name (" << line_name << ")" << ::std::endl; - ::gpiod::line line = ::gpiod::find_line(line_name); + auto line = ::gpiod::find_line(line_name); if (!line) throw ::std::runtime_error(line_name + " line not found"); - ::std::cout << "Requesting a single line" << ::std::endl; + ::std::cerr << "Requesting a single line" << ::std::endl; ::gpiod::line_request conf; conf.consumer = "gpiod_cxx_tests"; conf.request_type = ::gpiod::line_request::DIRECTION_OUTPUT; line.request(conf, 1); - ::std::cout << "Reading value" << ::std::endl; - ::std::cout << line.get_value() << ::std::endl; - ::std::cout << "Changing value to 0" << ::std::endl; + ::std::cerr << "Reading value" << ::std::endl; + ::std::cerr << line.get_value() << ::std::endl; + ::std::cerr << "Changing value to 0" << ::std::endl; line.set_value(0); - ::std::cout << "Reading value again" << ::std::endl; - ::std::cout << line.get_value() << ::std::endl; + ::std::cerr << "Reading value again" << ::std::endl; + ::std::cerr << line.get_value() << ::std::endl; } TEST_CASE(single_line_test); +void line_flags(void) +{ + ::gpiod::chip chip("gpiochip0"); + + auto line = chip.get_line(0); + + ::std::cerr << "line is used: " << boolstr(line.is_used()) << ::std::endl; + ::std::cerr << "line is requested: " << boolstr(line.is_requested()) << ::std::endl; + + ::std::cerr << "requesting line" << ::std::endl; + + ::gpiod::line_request config; + config.consumer = "gpiod_cxx_tests"; + config.request_type = ::gpiod::line_request::DIRECTION_OUTPUT; + config.flags = ::gpiod::line_request::FLAG_OPEN_DRAIN; + line.request(config); + + ::std::cerr << "line is used: " << boolstr(line.is_used()) << ::std::endl; + ::std::cerr << "line is open drain: " << boolstr(line.is_open_drain()) << ::std::endl; + ::std::cerr << "line is open source: " << boolstr(line.is_open_source()) << ::std::endl; + ::std::cerr << "line is requested: " << boolstr(line.is_requested()) << ::std::endl; + + if (!line.is_open_drain()) + throw ::std::logic_error("line should be open-drain"); +} +TEST_CASE(line_flags); + void multiple_lines_test(void) { ::gpiod::chip chip("gpiochip0"); - ::std::cout << "Getting multiple lines by offsets" << ::std::endl; + ::std::cerr << "Getting multiple lines by offsets" << ::std::endl; - ::gpiod::line_bulk lines = chip.get_lines({ 0, 2, 3, 4, 6 }); + auto lines = chip.get_lines({ 0, 2, 3, 4, 6 }); - ::std::cout << "Requesting them for output" << ::std::endl; + ::std::cerr << "Requesting them for output" << ::std::endl; ::gpiod::line_request config; config.consumer = "gpiod_cxx_tests"; @@ -164,46 +300,153 @@ void multiple_lines_test(void) lines.request(config); - ::std::cout << "Setting values" << ::std::endl; + ::std::cerr << "Setting values" << ::std::endl; lines.set_values({ 0, 1, 1, 0, 1}); - ::std::cout << "Requesting the lines for input" << ::std::endl; + ::std::cerr << "Requesting the lines for input" << ::std::endl; config.request_type = ::gpiod::line_request::DIRECTION_INPUT; lines.release(); lines.request(config); - ::std::cout << "Reading the values" << ::std::endl; + ::std::cerr << "Reading the values" << ::std::endl; auto vals = lines.get_values(); for (auto& it: vals) - ::std::cout << it << " "; - ::std::cout << ::std::endl; + ::std::cerr << it << " "; + ::std::cerr << ::std::endl; } TEST_CASE(multiple_lines_test); -void get_all_lines(void) +void chip_get_all_lines(void) { ::gpiod::chip chip("gpiochip0"); - ::std::cout << "Getting all lines from a chip" << ::std::endl; + ::std::cerr << "Getting all lines from a chip" << ::std::endl; auto lines = chip.get_all_lines(); for (auto& it: lines) - ::std::cout << "Offset: " << it.offset() << ::std::endl; + ::std::cerr << "Offset: " << it.offset() << ::std::endl; +} +TEST_CASE(chip_get_all_lines); + +void line_event_single_line(void) +{ + ::gpiod::chip chip("gpiochip0"); + + auto line = chip.get_line(1); + + ::gpiod::line_request config; + config.consumer = "gpiod_cxx_tests"; + config.request_type = ::gpiod::line_request::EVENT_BOTH_EDGES; + + ::std::cerr << "requesting line for events" << ::std::endl; + line.request(config); + + ::std::cerr << "generating a line event" << ::std::endl; + fire_line_event("gpiochip0", 1, true); + + if (!line.event_wait(::std::chrono::nanoseconds(1000000000))) + throw ::std::runtime_error("waiting for event timed out"); + + auto event = line.event_read(); + + ::std::cerr << "event received:" << ::std::endl; + print_event(event); +} +TEST_CASE(line_event_single_line); + +void line_event_multiple_lines(void) +{ + ::gpiod::chip chip("gpiochip0"); + + auto lines = chip.get_lines({ 1, 2, 3, 4, 5 }); + + ::gpiod::line_request config; + config.consumer = "gpiod_cxx_tests"; + config.request_type = ::gpiod::line_request::EVENT_BOTH_EDGES; + + ::std::cerr << "requesting lines for events" << ::std::endl; + lines.request(config); + + ::std::cerr << "generating two line events" << ::std::endl; + fire_line_event("gpiochip0", 1, true); + fire_line_event("gpiochip0", 2, true); + + auto event_lines = lines.event_wait(::std::chrono::nanoseconds(1000000000)); + if (!event_lines || event_lines.size() != 2) + throw ::std::runtime_error("expected two line events"); + + ::std::vector<::gpiod::line_event> events; + for (auto& line: event_lines) { + auto event = line.event_read(); + events.push_back(event); + } + + ::std::cerr << "events received:" << ::std::endl; + for (auto& event: events) + print_event(event); +} +TEST_CASE(line_event_multiple_lines); + +void line_event_poll_fd(void) +{ + ::std::map fd_line_map; + + ::gpiod::chip chip("gpiochip0"); + auto lines = chip.get_lines({ 1, 2, 3, 4, 5, 6 }); + + ::gpiod::line_request config; + config.consumer = "gpiod_cxx_tests"; + config.request_type = ::gpiod::line_request::EVENT_BOTH_EDGES; + + ::std::cerr << "requesting lines for events" << ::std::endl; + lines.request(config); + + ::std::cerr << "generating three line events" << ::std::endl; + fire_line_event("gpiochip0", 2, true); + fire_line_event("gpiochip0", 3, true); + fire_line_event("gpiochip0", 5, false); + + fd_line_map[lines[1].event_get_fd()] = lines[1]; + fd_line_map[lines[2].event_get_fd()] = lines[2]; + fd_line_map[lines[4].event_get_fd()] = lines[4]; + + pollfd fds[3]; + fds[0].fd = lines[1].event_get_fd(); + fds[1].fd = lines[2].event_get_fd(); + fds[2].fd = lines[4].event_get_fd(); + + fds[0].events = fds[1].events = fds[2].events = POLLIN | POLLPRI; + + int rv = poll(fds, 3, 1000); + if (rv < 0) + throw ::std::runtime_error("error polling for events: " + + ::std::string(::strerror(errno))); + else if (rv == 0) + throw ::std::runtime_error("poll() timed out while waiting for events"); + + if (rv != 3) + throw ::std::runtime_error("expected three line events"); + + ::std::cerr << "events received:" << ::std::endl; + for (unsigned int i = 0; i < 3; i++) { + if (fds[i].revents) + print_event(fd_line_map[fds[i].fd].event_read()); + } } -TEST_CASE(get_all_lines); +TEST_CASE(line_event_poll_fd); } /* namespace */ int main(int, char **) { for (auto& it: test_funcs) { - ::std::cout << "=================================================" << ::std::endl; - ::std::cout << it.first << ":\n" << ::std::endl; + ::std::cerr << "=================================================" << ::std::endl; + ::std::cerr << it.first << ":\n" << ::std::endl; it.second(); }