bindings: cxx: new test cases
authorBartosz Golaszewski <bartekgola@gmail.com>
Wed, 2 May 2018 10:19:39 +0000 (12:19 +0200)
committerBartosz Golaszewski <bartekgola@gmail.com>
Wed, 9 May 2018 16:00:39 +0000 (18:00 +0200)
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 <bartekgola@gmail.com>
bindings/cxx/examples/gpiod_cxx_tests.cpp

index 197b3e87d6cf35fe87f7edcb88e6d538a9f4f22c..4c6e859a91afe9613af74c13f0a12b9d0035cd2e 100644 (file)
 #include <stdexcept>
 #include <cstdlib>
 #include <iostream>
+#include <fstream>
+#include <map>
+#include <cstring>
+#include <cerrno>
+
+#include <poll.h>
 
 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<int, ::gpiod::line> 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();
        }