From 9413fe0baf6539cf9975d138388261b0b273562c Mon Sep 17 00:00:00 2001 From: Gabriel Gaessler Date: Sun, 2 Jan 2022 18:54:45 +0100 Subject: [PATCH] Add automated testing (via catkin_virtualenv) (#136) Automatically test the code on every push or PR by playing back data to the driver and test whether the expected amount of messages is outputted by the driver and the messages contain the expected data. The tester can cover more functionality that the current version of the nmea_navsat_driver supports, for these features new PRs will be created. The full usage can be observed in the boschresearch fork. Signed-off-by: Gabriel Gaessler --- .github/workflows/ci.yaml | 20 +++ CMakeLists.txt | 30 +++- package.xml | 8 + test/README.rst | 16 ++ test/configs/config_launch.yaml | 7 + test/configs/config_log.yaml | 15 ++ test/configs/config_tester.yaml | 8 + test/logs/nmea.log | 240 ++++++++++++++++++++++++++ test/requirements.txt | 1 + test/test_driver.py | 289 ++++++++++++++++++++++++++++++++ 10 files changed, 631 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/ci.yaml create mode 100644 test/README.rst create mode 100644 test/configs/config_launch.yaml create mode 100644 test/configs/config_log.yaml create mode 100644 test/configs/config_tester.yaml create mode 100644 test/logs/nmea.log create mode 100644 test/requirements.txt create mode 100644 test/test_driver.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..4cb7131 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,20 @@ +on: [push, pull_request] + +jobs: + test: + runs-on: [ubuntu-latest] + timeout-minutes: 10 + strategy: + matrix: + ros_distro: [noetic] + ubuntu_distro: [focal] + arch: [amd64] + steps: + - uses: ros-tooling/setup-ros@v0.2 + with: + required-ros-distributions: ${{ matrix.ros_distro }} + - name: build and test + uses: ros-tooling/action-ros-ci@v0.2 + with: + package-name: nmea_navsat_driver + target-ros1-distro: ${{ matrix.ros_distro }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cb8ff1..491ff4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,15 @@ cmake_minimum_required(VERSION 3.0.2) project(nmea_navsat_driver) -find_package(catkin REQUIRED) +if (NOT CATKIN_ENABLE_TESTING) + find_package(catkin REQUIRED COMPONENTS + ) +endif() +if (CATKIN_ENABLE_TESTING) + find_package(catkin REQUIRED COMPONENTS + catkin_virtualenv + ) +endif() catkin_python_setup() catkin_package() @@ -19,12 +27,28 @@ install(DIRECTORY launch/ FILES_MATCHING PATTERN "*.launch" ) -install(FILES LICENSE.txt +install(FILES + LICENSE.txt + test/requirements.txt DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} ) if (CATKIN_ENABLE_TESTING) - find_package(roslint) + catkin_generate_virtualenv( + PYTHON_INTERPRETER python3 + ) + + catkin_add_nosetests( + test/test_driver.py + DEPENDENCIES ${${PROJECT_NAME}_EXPORTED_TARGETS} ${PROJECT_NAME}_generate_virtualenv + ) + + catkin_install_python( + PROGRAMS + test/test_driver.py + DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}) + + find_package(roslint REQUIRED) roslint_python() roslint_add_test() endif() diff --git a/package.xml b/package.xml index 3328130..4df53d4 100644 --- a/package.xml +++ b/package.xml @@ -22,15 +22,23 @@ rospy python-serial python3-serial + python-rospkg + python3-rospkg geometry_msgs nmea_msgs sensor_msgs tf + python-yaml + python3-yaml + + catkin_virtualenv roslint + rostest + test/requirements.txt diff --git a/test/README.rst b/test/README.rst new file mode 100644 index 0000000..025433e --- /dev/null +++ b/test/README.rst @@ -0,0 +1,16 @@ +nmea_navsat_driver testing +========================== + +This folder contains a tester for the nmea_navsat_driver_package as well as configurations and GNSS device logs for playback. + +Configurations +-------------- + +* `config_launch.yaml` defines with launch files should be called, which interface they refer to and which messages are in scope. For `serial` a virtual serial port is created and for `tcp` a TCP-server is created. +* `config_logs.yaml` defines which pre-recorded logs shall be tested in which mode at which speed. And even more important which messages are to be expected from the driver in which quantity and which fields have to stay in which boundaries. These settings determine whether the test will result in pass or fail. Also defines which parameters shall be passed to the launch files. +* `config_tester.yaml` general settings of the tester. + +Running the test +---------------- + +The tester can be automatically executed via Github Actions, running the nosetests defined in the CMakeLists.txt eventually calling the rostest. The workflow configuration is therefore located at `.github/workflows/ci.yaml`. diff --git a/test/configs/config_launch.yaml b/test/configs/config_launch.yaml new file mode 100644 index 0000000..846a2d4 --- /dev/null +++ b/test/configs/config_launch.yaml @@ -0,0 +1,7 @@ +# list of all launch files to be tested +nmea_serial_driver.launch: + # interface, serial or TCP + interface: serial + # messages that are supported by this driver configuration + messages: + - NavSatFix diff --git a/test/configs/config_log.yaml b/test/configs/config_log.yaml new file mode 100644 index 0000000..80a57d1 --- /dev/null +++ b/test/configs/config_log.yaml @@ -0,0 +1,15 @@ +# list of all raw data logs that shall be tested +nmea.log: + # mode, nmea or binary (u-blox) + mode: nmea + # rate for nmea mode in lines per second + rate: 4 + # additional parameters to pass to the launch file + parameters: [] + topics: + fix: + _message_count: 120 + _type: NavSatFix + latitude: + max: 48.7734 + min: 48.7733 diff --git a/test/configs/config_tester.yaml b/test/configs/config_tester.yaml new file mode 100644 index 0000000..bda3a0e --- /dev/null +++ b/test/configs/config_tester.yaml @@ -0,0 +1,8 @@ +# speedup in comparison to real time playback +speedup_rate: 20 +# chunksize in bytes to read at once for binary data +binary_read_len: 10 +# tcp host +tcp_host: 127.0.0.1 +# tcp port to start with, will increment from here +tcp_port: 4242 diff --git a/test/logs/nmea.log b/test/logs/nmea.log new file mode 100644 index 0000000..8c32622 --- /dev/null +++ b/test/logs/nmea.log @@ -0,0 +1,240 @@ +$GNRMC,092542.00,A,4846.4008666,N,00854.7249096,E,0.011,,200420,,,R,V*01 +$GNGGA,092542.00,4846.4008666,N,00854.7249096,E,4,12,0.47,419.166,M,47.535,M,0.0,0000*66 +$GNRMC,092542.50,A,4846.4008639,N,00854.7249134,E,0.017,,200420,,,R,V*01 +$GNGGA,092542.50,4846.4008639,N,00854.7249134,E,4,12,0.47,419.164,M,47.535,M,0.0,0000*62 +$GNRMC,092543.00,A,4846.4008633,N,00854.7249165,E,0.016,,200420,,,R,V*0A +$GNGGA,092543.00,4846.4008633,N,00854.7249165,E,4,12,0.47,419.163,M,47.535,M,0.0,0000*6F +$GNRMC,092543.50,A,4846.4008626,N,00854.7249190,E,0.015,,200420,,,R,V*02 +$GNGGA,092543.50,4846.4008626,N,00854.7249190,E,4,12,0.47,419.164,M,47.535,M,0.0,0000*63 +$GNRMC,092544.00,A,4846.4008631,N,00854.7249227,E,0.015,,200420,,,R,V*09 +$GNGGA,092544.00,4846.4008631,N,00854.7249227,E,4,12,0.47,419.161,M,47.535,M,0.0,0000*6D +$GNRMC,092544.50,A,4846.4008623,N,00854.7249256,E,0.017,,200420,,,R,V*0B +$GNGGA,092544.50,4846.4008623,N,00854.7249256,E,4,12,0.48,419.159,M,47.535,M,0.0,0000*69 +$GNRMC,092545.00,A,4846.4008600,N,00854.7249288,E,0.017,,200420,,,R,V*0D +$GNGGA,092545.00,4846.4008600,N,00854.7249288,E,4,12,0.48,419.157,M,47.535,M,0.0,0000*61 +$GNRMC,092545.50,A,4846.4008591,N,00854.7249306,E,0.015,,200420,,,R,V*06 +$GNGGA,092545.50,4846.4008591,N,00854.7249306,E,4,12,0.48,419.155,M,47.535,M,0.0,0000*6A +$GNRMC,092546.00,A,4846.4008590,N,00854.7249360,E,0.013,,200420,,,R,V*07 +$GNGGA,092546.00,4846.4008590,N,00854.7249360,E,4,12,0.48,419.154,M,47.535,M,0.0,0000*6C +$GNRMC,092546.50,A,4846.4008571,N,00854.7249377,E,0.020,,200420,,,R,V*0B +$GNGGA,092546.50,4846.4008571,N,00854.7249377,E,4,12,0.48,419.151,M,47.535,M,0.0,0000*65 +$GNRMC,092547.00,A,4846.4008566,N,00854.7249403,E,0.020,,200420,,,R,V*0D +$GNGGA,092547.00,4846.4008566,N,00854.7249403,E,4,12,0.48,419.149,M,47.535,M,0.0,0000*6A +$GNRMC,092547.50,A,4846.4008550,N,00854.7249424,E,0.010,,200420,,,R,V*0B +$GNGGA,092547.50,4846.4008550,N,00854.7249424,E,4,12,0.48,419.150,M,47.535,M,0.0,0000*67 +$GNRMC,092548.00,A,4846.4008544,N,00854.7249474,E,0.019,,200420,,,R,V*08 +$GNGGA,092548.00,4846.4008544,N,00854.7249474,E,4,12,0.48,419.150,M,47.535,M,0.0,0000*6D +$GNRMC,092548.50,A,4846.4008526,N,00854.7249500,E,0.018,,200420,,,R,V*0A +$GNGGA,092548.50,4846.4008526,N,00854.7249500,E,4,12,0.48,419.147,M,47.535,M,0.0,0000*68 +$GNRMC,092549.00,A,4846.4008512,N,00854.7249513,E,0.017,,200420,,,R,V*04 +$GNGGA,092549.00,4846.4008512,N,00854.7249513,E,4,12,0.48,419.140,M,47.535,M,0.0,0000*6E +$GNRMC,092549.50,A,4846.4008503,N,00854.7249550,E,0.010,,200420,,,R,V*01 +$GNGGA,092549.50,4846.4008503,N,00854.7249550,E,4,12,0.48,419.134,M,47.535,M,0.0,0000*6F +$GNRMC,092550.00,A,4846.4008488,N,00854.7249571,E,0.013,,200420,,,R,V*0E +$GNGGA,092550.00,4846.4008488,N,00854.7249571,E,4,12,0.48,419.130,M,47.535,M,0.0,0000*67 +$GNRMC,092550.50,A,4846.4008478,N,00854.7249592,E,0.020,,200420,,,R,V*09 +$GNGGA,092550.50,4846.4008478,N,00854.7249592,E,4,12,0.48,419.126,M,47.535,M,0.0,0000*67 +$GNRMC,092551.00,A,4846.4008460,N,00854.7249618,E,0.023,,200420,,,R,V*06 +$GNGGA,092551.00,4846.4008460,N,00854.7249618,E,4,12,0.48,419.122,M,47.535,M,0.0,0000*6F +$GNRMC,092551.50,A,4846.4008450,N,00854.7249652,E,0.019,,200420,,,R,V*07 +$GNGGA,092551.50,4846.4008450,N,00854.7249652,E,4,12,0.48,419.116,M,47.535,M,0.0,0000*60 +$GNRMC,092552.00,A,4846.4008435,N,00854.7249686,E,0.018,,200420,,,R,V*0A +$GNGGA,092552.00,4846.4008435,N,00854.7249686,E,4,12,0.48,419.110,M,47.535,M,0.0,0000*6A +$GNRMC,092552.50,A,4846.4008425,N,00854.7249710,E,0.017,,200420,,,R,V*0F +$GNGGA,092552.50,4846.4008425,N,00854.7249710,E,4,12,0.49,419.106,M,47.535,M,0.0,0000*66 +$GNRMC,092553.00,A,4846.4008409,N,00854.7249738,E,0.012,,200420,,,R,V*0A +$GNGGA,092553.00,4846.4008409,N,00854.7249738,E,4,12,0.49,419.099,M,47.535,M,0.0,0000*61 +$GNRMC,092553.50,A,4846.4008405,N,00854.7249753,E,0.018,,200420,,,R,V*04 +$GNGGA,092553.50,4846.4008405,N,00854.7249753,E,4,12,0.49,419.096,M,47.535,M,0.0,0000*6A +$GNRMC,092554.00,A,4846.4008401,N,00854.7249790,E,0.014,,200420,,,R,V*01 +$GNGGA,092554.00,4846.4008401,N,00854.7249790,E,4,12,0.49,419.093,M,47.535,M,0.0,0000*66 +$GNRMC,092554.50,A,4846.4008388,N,00854.7249812,E,0.014,,200420,,,R,V*07 +$GNGGA,092554.50,4846.4008388,N,00854.7249812,E,4,12,0.49,419.091,M,47.535,M,0.0,0000*62 +$GNRMC,092555.00,A,4846.4008374,N,00854.7249826,E,0.014,,200420,,,R,V*07 +$GNGGA,092555.00,4846.4008374,N,00854.7249826,E,4,12,0.49,419.095,M,47.535,M,0.0,0000*66 +$GNRMC,092555.50,A,4846.4008347,N,00854.7249843,E,0.013,,200420,,,R,V*06 +$GNGGA,092555.50,4846.4008347,N,00854.7249843,E,4,12,0.49,419.097,M,47.535,M,0.0,0000*62 +$GNRMC,092556.00,A,4846.4008345,N,00854.7249864,E,0.012,,200420,,,R,V*06 +$GNGGA,092556.00,4846.4008345,N,00854.7249864,E,4,12,0.50,419.095,M,47.535,M,0.0,0000*69 +$GNRMC,092556.50,A,4846.4008324,N,00854.7249876,E,0.014,,200420,,,R,V*01 +$GNGGA,092556.50,4846.4008324,N,00854.7249876,E,4,12,0.50,419.093,M,47.535,M,0.0,0000*6E +$GNRMC,092557.00,A,4846.4008316,N,00854.7249899,E,0.016,,200420,,,R,V*07 +$GNGGA,092557.00,4846.4008316,N,00854.7249899,E,4,12,0.50,419.094,M,47.535,M,0.0,0000*6D +$GNRMC,092557.50,A,4846.4008301,N,00854.7249902,E,0.017,,200420,,,R,V*06 +$GNGGA,092557.50,4846.4008301,N,00854.7249902,E,4,12,0.50,419.095,M,47.535,M,0.0,0000*6C +$GNRMC,092558.00,A,4846.4008285,N,00854.7249918,E,0.011,,200420,,,R,V*0C +$GNGGA,092558.00,4846.4008285,N,00854.7249918,E,4,12,0.50,419.095,M,47.535,M,0.0,0000*60 +$GNRMC,092558.50,A,4846.4008280,N,00854.7249943,E,0.010,,200420,,,R,V*03 +$GNGGA,092558.50,4846.4008280,N,00854.7249943,E,4,12,0.50,419.094,M,47.535,M,0.0,0000*6F +$GNRMC,092559.00,A,4846.4008279,N,00854.7249973,E,0.012,,200420,,,R,V*00 +$GNGGA,092559.00,4846.4008279,N,00854.7249973,E,4,12,0.50,419.090,M,47.535,M,0.0,0000*6A +$GNRMC,092559.50,A,4846.4008265,N,00854.7250015,E,0.017,,200420,,,R,V*0C +$GNGGA,092559.50,4846.4008265,N,00854.7250015,E,4,12,0.50,419.089,M,47.535,M,0.0,0000*6B +$GNRMC,092600.00,A,4846.4008244,N,00854.7250041,E,0.016,,200420,,,R,V*05 +$GNGGA,092600.00,4846.4008244,N,00854.7250041,E,4,12,0.50,419.092,M,47.535,M,0.0,0000*69 +$GNRMC,092600.50,A,4846.4008243,N,00854.7250068,E,0.016,,200420,,,R,V*0C +$GNGGA,092600.50,4846.4008243,N,00854.7250068,E,4,12,0.50,419.086,M,47.535,M,0.0,0000*65 +$GNRMC,092601.00,A,4846.4008227,N,00854.7250103,E,0.018,,200420,,,R,V*08 +$GNGGA,092601.00,4846.4008227,N,00854.7250103,E,4,12,0.50,419.085,M,47.535,M,0.0,0000*6C +$GNRMC,092601.50,A,4846.4008212,N,00854.7250134,E,0.008,,200420,,,R,V*0E +$GNGGA,092601.50,4846.4008212,N,00854.7250134,E,4,12,0.50,419.085,M,47.535,M,0.0,0000*6B +$GNRMC,092602.00,A,4846.4008195,N,00854.7250185,E,0.007,,200420,,,R,V*01 +$GNGGA,092602.00,4846.4008195,N,00854.7250185,E,4,12,0.50,419.082,M,47.535,M,0.0,0000*6C +$GNRMC,092602.50,A,4846.4008203,N,00854.7250211,E,0.011,,200420,,,R,V*01 +$GNGGA,092602.50,4846.4008203,N,00854.7250211,E,4,12,0.50,419.077,M,47.535,M,0.0,0000*61 +$GNRMC,092603.00,A,4846.4008185,N,00854.7250245,E,0.016,,200420,,,R,V*0E +$GNGGA,092603.00,4846.4008185,N,00854.7250245,E,4,12,0.50,419.078,M,47.535,M,0.0,0000*66 +$GNRMC,092603.50,A,4846.4008178,N,00854.7250270,E,0.012,,200420,,,R,V*0B +$GNGGA,092603.50,4846.4008178,N,00854.7250270,E,4,12,0.50,419.077,M,47.535,M,0.0,0000*68 +$GNRMC,092604.00,A,4846.4008167,N,00854.7250308,E,0.009,,200420,,,R,V*03 +$GNGGA,092604.00,4846.4008167,N,00854.7250308,E,4,12,0.50,419.074,M,47.535,M,0.0,0000*69 +$GNRMC,092604.50,A,4846.4008163,N,00854.7250343,E,0.007,,200420,,,R,V*03 +$GNGGA,092604.50,4846.4008163,N,00854.7250343,E,4,12,0.50,419.072,M,47.535,M,0.0,0000*61 +$GNRMC,092605.00,A,4846.4008151,N,00854.7250389,E,0.015,,200420,,,R,V*03 +$GNGGA,092605.00,4846.4008151,N,00854.7250389,E,4,12,0.50,419.073,M,47.535,M,0.0,0000*63 +$GNRMC,092605.50,A,4846.4008133,N,00854.7250437,E,0.010,,200420,,,R,V*05 +$GNGGA,092605.50,4846.4008133,N,00854.7250437,E,4,12,0.50,419.073,M,47.535,M,0.0,0000*60 +$GNRMC,092606.00,A,4846.4008136,N,00854.7250462,E,0.005,,200420,,,R,V*02 +$GNGGA,092606.00,4846.4008136,N,00854.7250462,E,4,12,0.50,419.076,M,47.535,M,0.0,0000*66 +$GNRMC,092606.50,A,4846.4008130,N,00854.7250486,E,0.009,,200420,,,R,V*07 +$GNGGA,092606.50,4846.4008130,N,00854.7250486,E,4,12,0.50,419.078,M,47.535,M,0.0,0000*61 +$GNRMC,092607.00,A,4846.4008114,N,00854.7250519,E,0.008,,200420,,,R,V*03 +$GNGGA,092607.00,4846.4008114,N,00854.7250519,E,4,12,0.50,419.077,M,47.535,M,0.0,0000*6B +$GNRMC,092607.50,A,4846.4008119,N,00854.7250548,E,0.011,,200420,,,R,V*07 +$GNGGA,092607.50,4846.4008119,N,00854.7250548,E,4,12,0.50,419.079,M,47.535,M,0.0,0000*69 +$GNRMC,092608.00,A,4846.4008101,N,00854.7250572,E,0.014,,200420,,,R,V*08 +$GNGGA,092608.00,4846.4008101,N,00854.7250572,E,4,12,0.50,419.080,M,47.535,M,0.0,0000*65 +$GNRMC,092608.50,A,4846.4008083,N,00854.7250614,E,0.015,,200420,,,R,V*04 +$GNGGA,092608.50,4846.4008083,N,00854.7250614,E,4,12,0.50,419.082,M,47.535,M,0.0,0000*6A +$GNRMC,092609.00,A,4846.4008072,N,00854.7250651,E,0.009,,200420,,,R,V*02 +$GNGGA,092609.00,4846.4008072,N,00854.7250651,E,4,12,0.50,419.086,M,47.535,M,0.0,0000*65 +$GNRMC,092609.50,A,4846.4008066,N,00854.7250677,E,0.011,,200420,,,R,V*0F +$GNGGA,092609.50,4846.4008066,N,00854.7250677,E,4,12,0.50,419.085,M,47.535,M,0.0,0000*62 +$GNRMC,092610.00,A,4846.4008049,N,00854.7250708,E,0.017,,200420,,,R,V*00 +$GNGGA,092610.00,4846.4008049,N,00854.7250708,E,4,12,0.50,419.093,M,47.535,M,0.0,0000*6C +$GNRMC,092610.50,A,4846.4008044,N,00854.7250739,E,0.007,,200420,,,R,V*0B +$GNGGA,092610.50,4846.4008044,N,00854.7250739,E,4,12,0.50,419.097,M,47.535,M,0.0,0000*62 +$GNRMC,092611.00,A,4846.4008032,N,00854.7250735,E,0.012,,200420,,,R,V*06 +$GNGGA,092611.00,4846.4008032,N,00854.7250735,E,4,12,0.50,419.103,M,47.535,M,0.0,0000*67 +$GNRMC,092611.50,A,4846.4008035,N,00854.7250749,E,0.015,,200420,,,R,V*08 +$GNGGA,092611.50,4846.4008035,N,00854.7250749,E,4,12,0.50,419.109,M,47.535,M,0.0,0000*64 +$GNRMC,092612.00,A,4846.4008033,N,00854.7250771,E,0.016,,200420,,,R,V*00 +$GNGGA,092612.00,4846.4008033,N,00854.7250771,E,4,12,0.50,419.110,M,47.535,M,0.0,0000*67 +$GNRMC,092612.50,A,4846.4008028,N,00854.7250788,E,0.010,,200420,,,R,V*0F +$GNGGA,092612.50,4846.4008028,N,00854.7250788,E,4,12,0.50,419.109,M,47.535,M,0.0,0000*66 +$GNRMC,092613.00,A,4846.4008016,N,00854.7250800,E,0.024,,200420,,,R,V*0E +$GNGGA,092613.00,4846.4008016,N,00854.7250800,E,4,12,0.50,419.112,M,47.535,M,0.0,0000*6A +$GNRMC,092613.50,A,4846.4008002,N,00854.7250804,E,0.014,,200420,,,R,V*09 +$GNGGA,092613.50,4846.4008002,N,00854.7250804,E,4,12,0.50,419.111,M,47.535,M,0.0,0000*6D +$GNRMC,092614.00,A,4846.4008007,N,00854.7250794,E,0.010,,200420,,,R,V*0C +$GNGGA,092614.00,4846.4008007,N,00854.7250794,E,4,12,0.50,419.119,M,47.535,M,0.0,0000*64 +$GNRMC,092614.50,A,4846.4007990,N,00854.7250796,E,0.007,,200420,,,R,V*05 +$GNGGA,092614.50,4846.4007990,N,00854.7250796,E,4,12,0.50,419.119,M,47.535,M,0.0,0000*6B +$GNRMC,092615.00,A,4846.4007977,N,00854.7250797,E,0.008,,200420,,,R,V*06 +$GNGGA,092615.00,4846.4007977,N,00854.7250797,E,4,12,0.50,419.126,M,47.535,M,0.0,0000*6B +$GNRMC,092615.50,A,4846.4007977,N,00854.7250808,E,0.009,,200420,,,R,V*0B +$GNGGA,092615.50,4846.4007977,N,00854.7250808,E,4,12,0.50,419.129,M,47.535,M,0.0,0000*68 +$GNRMC,092616.00,A,4846.4007967,N,00854.7250824,E,0.008,,200420,,,R,V*03 +$GNGGA,092616.00,4846.4007967,N,00854.7250824,E,4,12,0.50,419.127,M,47.535,M,0.0,0000*6F +$GNRMC,092616.50,A,4846.4007948,N,00854.7250838,E,0.009,,200420,,,R,V*07 +$GNGGA,092616.50,4846.4007948,N,00854.7250838,E,4,12,0.50,419.134,M,47.535,M,0.0,0000*68 +$GNRMC,092617.00,A,4846.4007940,N,00854.7250871,E,0.005,,200420,,,R,V*0A +$GNGGA,092617.00,4846.4007940,N,00854.7250871,E,4,12,0.50,419.132,M,47.535,M,0.0,0000*6F +$GNRMC,092617.50,A,4846.4007937,N,00854.7250885,E,0.017,,200420,,,R,V*07 +$GNGGA,092617.50,4846.4007937,N,00854.7250885,E,4,12,0.50,419.129,M,47.535,M,0.0,0000*6B +$GNRMC,092618.00,A,4846.4007930,N,00854.7250886,E,0.015,,200420,,,R,V*0B +$GNGGA,092618.00,4846.4007930,N,00854.7250886,E,4,12,0.50,419.132,M,47.535,M,0.0,0000*6F +$GNRMC,092618.50,A,4846.4007914,N,00854.7250905,E,0.015,,200420,,,R,V*02 +$GNGGA,092618.50,4846.4007914,N,00854.7250905,E,4,12,0.50,419.128,M,47.535,M,0.0,0000*6D +$GNRMC,092619.00,A,4846.4007904,N,00854.7250928,E,0.012,,200420,,,R,V*0F +$GNGGA,092619.00,4846.4007904,N,00854.7250928,E,4,12,0.50,419.127,M,47.535,M,0.0,0000*68 +$GNRMC,092619.50,A,4846.4007896,N,00854.7250932,E,0.015,,200420,,,R,V*0C +$GNGGA,092619.50,4846.4007896,N,00854.7250932,E,4,12,0.49,419.125,M,47.535,M,0.0,0000*66 +$GNRMC,092620.00,A,4846.4007884,N,00854.7250964,E,0.011,,200420,,,R,V*07 +$GNGGA,092620.00,4846.4007884,N,00854.7250964,E,4,12,0.49,419.126,M,47.535,M,0.0,0000*6A +$GNRMC,092620.50,A,4846.4007869,N,00854.7250979,E,0.013,,200420,,,R,V*0F +$GNGGA,092620.50,4846.4007869,N,00854.7250979,E,4,12,0.49,419.122,M,47.535,M,0.0,0000*64 +$GNRMC,092621.00,A,4846.4007843,N,00854.7251004,E,0.010,,200420,,,R,V*02 +$GNGGA,092621.00,4846.4007843,N,00854.7251004,E,4,12,0.49,419.124,M,47.535,M,0.0,0000*6C +$GNRMC,092621.50,A,4846.4007837,N,00854.7251027,E,0.013,,200420,,,R,V*06 +$GNGGA,092621.50,4846.4007837,N,00854.7251027,E,4,12,0.49,419.119,M,47.535,M,0.0,0000*65 +$GNRMC,092622.00,A,4846.4007820,N,00854.7251021,E,0.016,,200420,,,R,V*05 +$GNGGA,092622.00,4846.4007820,N,00854.7251021,E,4,12,0.49,419.120,M,47.535,M,0.0,0000*69 +$GNRMC,092622.50,A,4846.4007816,N,00854.7251013,E,0.011,,200420,,,R,V*03 +$GNGGA,092622.50,4846.4007816,N,00854.7251013,E,4,12,0.49,419.118,M,47.535,M,0.0,0000*63 +$GNRMC,092623.00,A,4846.4007795,N,00854.7251014,E,0.011,,200420,,,R,V*04 +$GNGGA,092623.00,4846.4007795,N,00854.7251014,E,4,12,0.49,419.122,M,47.535,M,0.0,0000*6D +$GNRMC,092623.50,A,4846.4007781,N,00854.7251019,E,0.017,,200420,,,R,V*0F +$GNGGA,092623.50,4846.4007781,N,00854.7251019,E,4,12,0.49,419.123,M,47.535,M,0.0,0000*61 +$GNRMC,092624.00,A,4846.4007777,N,00854.7251037,E,0.016,,200420,,,R,V*09 +$GNGGA,092624.00,4846.4007777,N,00854.7251037,E,4,12,0.49,419.119,M,47.535,M,0.0,0000*6F +$GNRMC,092624.50,A,4846.4007764,N,00854.7251040,E,0.017,,200420,,,R,V*0F +$GNGGA,092624.50,4846.4007764,N,00854.7251040,E,4,12,0.49,419.120,M,47.535,M,0.0,0000*62 +$GNRMC,092625.00,A,4846.4007762,N,00854.7251050,E,0.008,,200420,,,R,V*02 +$GNGGA,092625.00,4846.4007762,N,00854.7251050,E,4,12,0.49,419.121,M,47.535,M,0.0,0000*60 +$GNRMC,092625.50,A,4846.4007754,N,00854.7251061,E,0.013,,200420,,,R,V*0A +$GNGGA,092625.50,4846.4007754,N,00854.7251061,E,4,12,0.50,419.122,M,47.535,M,0.0,0000*69 +$GNRMC,092626.00,A,4846.4007742,N,00854.7251083,E,0.013,,200420,,,R,V*07 +$GNGGA,092626.00,4846.4007742,N,00854.7251083,E,4,12,0.50,419.116,M,47.535,M,0.0,0000*63 +$GNRMC,092626.50,A,4846.4007722,N,00854.7251091,E,0.010,,200420,,,R,V*04 +$GNGGA,092626.50,4846.4007722,N,00854.7251091,E,4,12,0.50,419.110,M,47.535,M,0.0,0000*65 +$GNRMC,092627.00,A,4846.4007709,N,00854.7251094,E,0.019,,200420,,,R,V*05 +$GNGGA,092627.00,4846.4007709,N,00854.7251094,E,4,12,0.50,419.110,M,47.535,M,0.0,0000*6D +$GNRMC,092627.50,A,4846.4007675,N,00854.7251109,E,0.015,,200420,,,R,V*03 +$GNGGA,092627.50,4846.4007675,N,00854.7251109,E,4,12,0.50,419.110,M,47.535,M,0.0,0000*67 +$GNRMC,092628.00,A,4846.4007669,N,00854.7251105,E,0.014,,200420,,,R,V*09 +$GNGGA,092628.00,4846.4007669,N,00854.7251105,E,4,12,0.50,419.103,M,47.535,M,0.0,0000*6E +$GNRMC,092628.50,A,4846.4007646,N,00854.7251117,E,0.010,,200420,,,R,V*06 +$GNGGA,092628.50,4846.4007646,N,00854.7251117,E,4,12,0.50,419.100,M,47.535,M,0.0,0000*66 +$GNRMC,092629.00,A,4846.4007632,N,00854.7251127,E,0.016,,200420,,,R,V*04 +$GNGGA,092629.00,4846.4007632,N,00854.7251127,E,4,12,0.50,419.097,M,47.535,M,0.0,0000*6D +$GNRMC,092629.50,A,4846.4007617,N,00854.7251132,E,0.013,,200420,,,R,V*07 +$GNGGA,092629.50,4846.4007617,N,00854.7251132,E,4,12,0.50,419.097,M,47.535,M,0.0,0000*6B +$GNRMC,092630.00,A,4846.4007603,N,00854.7251133,E,0.021,,200420,,,R,V*0F +$GNGGA,092630.00,4846.4007603,N,00854.7251133,E,4,12,0.50,419.099,M,47.535,M,0.0,0000*6C +$GNRMC,092630.50,A,4846.4007601,N,00854.7251133,E,0.014,,200420,,,R,V*0E +$GNGGA,092630.50,4846.4007601,N,00854.7251133,E,4,12,0.50,419.100,M,47.535,M,0.0,0000*6A +$GNRMC,092631.00,A,4846.4007598,N,00854.7251112,E,0.010,,200420,,,R,V*0E +$GNGGA,092631.00,4846.4007598,N,00854.7251112,E,4,12,0.50,419.100,M,47.535,M,0.0,0000*6E +$GNRMC,092631.50,A,4846.4007574,N,00854.7251119,E,0.015,,200420,,,R,V*07 +$GNGGA,092631.50,4846.4007574,N,00854.7251119,E,4,12,0.50,419.099,M,47.535,M,0.0,0000*63 +$GNRMC,092632.00,A,4846.4007556,N,00854.7251100,E,0.007,,200420,,,R,V*0A +$GNGGA,092632.00,4846.4007556,N,00854.7251100,E,4,12,0.49,419.098,M,47.535,M,0.0,0000*64 +$GNRMC,092632.50,A,4846.4007531,N,00854.7251109,E,0.013,,200420,,,R,V*02 +$GNGGA,092632.50,4846.4007531,N,00854.7251109,E,4,12,0.51,419.091,M,47.535,M,0.0,0000*69 +$GNRMC,092633.00,A,4846.4007509,N,00854.7251111,E,0.018,,200420,,,R,V*0F +$GNGGA,092633.00,4846.4007509,N,00854.7251111,E,4,12,0.51,419.089,M,47.535,M,0.0,0000*66 +$GNRMC,092633.50,A,4846.4007482,N,00854.7251102,E,0.014,,200420,,,R,V*06 +$GNGGA,092633.50,4846.4007482,N,00854.7251102,E,4,12,0.51,419.084,M,47.535,M,0.0,0000*6E +$GNRMC,092634.00,A,4846.4007472,N,00854.7251109,E,0.015,,200420,,,R,V*01 +$GNGGA,092634.00,4846.4007472,N,00854.7251109,E,4,12,0.51,419.077,M,47.535,M,0.0,0000*64 +$GNRMC,092634.50,A,4846.4007448,N,00854.7251106,E,0.018,,200420,,,R,V*0F +$GNGGA,092634.50,4846.4007448,N,00854.7251106,E,4,12,0.50,419.073,M,47.535,M,0.0,0000*62 +$GNRMC,092635.00,A,4846.4007452,N,00854.7251093,E,0.013,,200420,,,R,V*06 +$GNGGA,092635.00,4846.4007452,N,00854.7251093,E,4,12,0.50,419.069,M,47.535,M,0.0,0000*6B +$GNRMC,092635.50,A,4846.4007434,N,00854.7251100,E,0.018,,200420,,,R,V*03 +$GNGGA,092635.50,4846.4007434,N,00854.7251100,E,4,12,0.50,419.067,M,47.535,M,0.0,0000*6B +$GNRMC,092636.00,A,4846.4007439,N,00854.7251108,E,0.018,,200420,,,R,V*00 +$GNGGA,092636.00,4846.4007439,N,00854.7251108,E,4,12,0.50,419.066,M,47.535,M,0.0,0000*69 +$GNRMC,092636.50,A,4846.4007430,N,00854.7251101,E,0.013,,200420,,,R,V*0E +$GNGGA,092636.50,4846.4007430,N,00854.7251101,E,4,12,0.50,419.065,M,47.535,M,0.0,0000*6F +$GNRMC,092637.00,A,4846.4007430,N,00854.7251111,E,0.007,,200420,,,R,V*0E +$GNGGA,092637.00,4846.4007430,N,00854.7251111,E,4,12,0.50,419.060,M,47.535,M,0.0,0000*6F +$GNRMC,092637.50,A,4846.4007428,N,00854.7251131,E,0.009,,200420,,,R,V*0E +$GNGGA,092637.50,4846.4007428,N,00854.7251131,E,4,12,0.50,419.054,M,47.535,M,0.0,0000*66 +$GNRMC,092638.00,A,4846.4007428,N,00854.7251129,E,0.033,,200420,,,R,V*04 +$GNGGA,092638.00,4846.4007428,N,00854.7251129,E,4,12,0.50,419.047,M,47.535,M,0.0,0000*67 +$GNRMC,092638.50,A,4846.4007424,N,00854.7251129,E,0.012,,200420,,,R,V*0E +$GNGGA,092638.50,4846.4007424,N,00854.7251129,E,4,12,0.50,419.047,M,47.535,M,0.0,0000*6E +$GNRMC,092639.00,A,4846.4007405,N,00854.7251138,E,0.015,,200420,,,R,V*0E +$GNGGA,092639.00,4846.4007405,N,00854.7251138,E,4,12,0.50,419.044,M,47.535,M,0.0,0000*6A +$GNRMC,092639.50,A,4846.4007407,N,00854.7251136,E,0.011,,200420,,,R,V*03 +$GNGGA,092639.50,4846.4007407,N,00854.7251136,E,4,12,0.50,419.038,M,47.535,M,0.0,0000*68 +$GNRMC,092640.00,A,4846.4007395,N,00854.7251138,E,0.014,,200420,,,R,V*0F +$GNGGA,092640.00,4846.4007395,N,00854.7251138,E,4,12,0.50,419.033,M,47.535,M,0.0,0000*6A +$GNRMC,092640.50,A,4846.4007400,N,00854.7251143,E,0.014,,200420,,,R,V*0D +$GNGGA,092640.50,4846.4007400,N,00854.7251143,E,4,12,0.50,419.025,M,47.535,M,0.0,0000*6F +$GNRMC,092641.00,A,4846.4007400,N,00854.7251154,E,0.006,,200420,,,R,V*0C +$GNGGA,092641.00,4846.4007400,N,00854.7251154,E,4,12,0.50,419.021,M,47.535,M,0.0,0000*69 +$GNRMC,092641.50,A,4846.4007406,N,00854.7251148,E,0.011,,200420,,,R,V*04 +$GNGGA,092641.50,4846.4007406,N,00854.7251148,E,4,12,0.50,419.015,M,47.535,M,0.0,0000*60 diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 0000000..bfa89ad --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1 @@ +PyVirtualSerialPorts==1.0.1 diff --git a/test/test_driver.py b/test/test_driver.py new file mode 100644 index 0000000..c144d9e --- /dev/null +++ b/test/test_driver.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python3 +"""Testing node for nmea_navsat_driver.""" + +# Software License Agreement (BSD License) +# +# Copyright (c) 2020-2021, Robert Bosch GmbH +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the names of the authors nor the names of their +# affiliated organizations may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import io +import os +import socket +import subprocess +import sys +import threading +import unittest +from contextlib import redirect_stdout + +import rospkg +import rospy +import rostest +import serial +import virtualserialports +import yaml +from sensor_msgs.msg import * + + +class TestDriver(unittest.TestCase): + """Test nmea_navsat_driver.""" + + def initialize(self): + """Initialize class.""" + with open(rospkg.RosPack().get_path('nmea_navsat_driver') + '/test/configs/config_log.yaml') as f: + self.cfg_logs = yaml.safe_load(f) + with open(rospkg.RosPack().get_path('nmea_navsat_driver') + '/test/configs/config_launch.yaml') as f: + self.cfg_launches = yaml.safe_load(f) + with open(rospkg.RosPack().get_path('nmea_navsat_driver') + '/test/configs/config_tester.yaml') as f: + self.cfg_tester = yaml.safe_load(f) + + self.test_failed = False + + # tcp ports can't be re-used in quick succession, therefore cache it + # here to increment it later + self.tcp_port = self.cfg_tester['tcp_port'] + + def cb(self, msg, args): + """Calback for all messages.""" + log, topic = args + + if topic not in self.msg_counter.keys(): + self.msg_counter[topic] = 0 + + self.msg_counter[topic] += 1 + self.check_msg(msg, log, topic) + + def check_msg(self, msg, log, topic): + """Check whether message content complies with target specs.""" + topics = self.cfg_logs[log]['topics'] + + if topic in topics.keys(): + for attribute in topics[topic]: + if not attribute.startswith('_'): + val = getattr(msg, attribute) + + if 'min' in topics[topic][attribute].keys(): + if val < topics[topic][attribute]['min']: + self.errors.append( + '{} {} {} {}: violating minimium value {}: {}'.format(log, msg.header.stamp.to_sec(), + topic, attribute, + topics[topic][attribute]['min'], + val)) + if 'max' in topics[topic][attribute].keys(): + if val > topics[topic][attribute]['max']: + self.errors.append( + '{} {} {} {}: violating maximum value {}: {}'.format(log, msg.header.stamp.to_sec(), + topic, attribute, + topics[topic][attribute]['max'], + val)) + if 'val' in topics[topic][attribute].keys(): + if val != topics[topic][attribute]['val']: + self.errors.append( + '{} {} {} {}: violating value {}: {}'.format(log, msg.header.stamp.to_sec(), topic, + attribute, topics[topic][attribute]['val'], + val)) + + @staticmethod + def create_vsps(): + """Create virtual serial ports (vsp) for serial driver testing.""" + f = io.StringIO() + with redirect_stdout(f): + th_vsp = threading.Thread(target=virtualserialports.run, args=(2, False, False)) + th_vsp.daemon = True + th_vsp.start() + + return f.getvalue().split('\n')[:-1] + + def playback_log(self, launchfile, log): + """Playback a logfile.""" + playback_path = rospkg.RosPack().get_path('nmea_navsat_driver') + '/test/logs/' + log + + if self.cfg_launches[launchfile]['interface'] == 'serial': + if self.cfg_logs[log]['mode'] == 'nmea': + with open(playback_path, 'r') as f: + for line in f: + self.serial_writer.write(str(line).encode('utf-8')) + rospy.sleep(1. / self.cfg_logs[log]['rate'] / self.cfg_tester['speedup_rate']) + elif self.cfg_logs[log]['mode'] == 'binary': + with open(playback_path, 'rb') as f: + byte = f.read(self.cfg_tester['binary_read_len']) + while byte: + self.serial_writer.write(byte) + rospy.sleep(1. / self.cfg_logs[log]['rate'] / self.cfg_tester['speedup_rate'] * + self.cfg_tester['binary_read_len']) + byte = f.read(self.cfg_tester['binary_read_len']) + elif self.cfg_launches[launchfile]['interface'] == 'tcp': + if self.cfg_logs[log]['mode'] == 'nmea': + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind((self.cfg_tester['tcp_host'], self.tcp_port)) + s.listen() + conn, addr = s.accept() + with conn: + rospy.loginfo('TCP driver connected') + if self.cfg_logs[log]['mode'] == 'nmea': + with open(playback_path, 'r') as f: + for line in f: + conn.send(str(line).encode('utf-8')) + rospy.sleep(1. / self.cfg_logs[log]['rate'] / self.cfg_tester['speedup_rate']) + + self.tcp_port += 1 + + def print_errors(self, launchfile, log, msg_types): + """Print errors.""" + for topic in self.cfg_logs[log]['topics']: + msg_count_target = self.cfg_logs[log]['topics'][topic]['_message_count'] + if msg_types[topic] not in self.cfg_launches[launchfile]['messages']: + rospy.loginfo( + '{}: skipping topic {} since not in scope for launch file {}'.format(log, topic, launchfile)) + elif topic not in self.msg_counter: + self.errors.append('{}: topic "{}" not published for log'.format(log, topic)) + elif self.msg_counter[topic] != msg_count_target: + self.errors.append( + '{}: unmet target message count {}: {} for topic {}'.format(log, msg_count_target, + self.msg_counter[topic], topic)) + + if len(self.errors) == 0: + rospy.logwarn('{}: no errors detected'.format(log)) + else: + for error in self.errors: + rospy.logerr(error) + + def test(self): + """Run the tester.""" + self.initialize() + + # init ros + ros_master_uri = 'http://127.0.0.1:11311' + env_variables = os.environ.copy() + env_variables['ROS_MASTER_URI'] = ros_master_uri + p_roscore = subprocess.Popen(['roscore'], env=env_variables) + rospy.init_node('nmea_navsat_driver_tester') + + # init serial handler and wait until vsps are ready + ports = self.create_vsps() + self.serial_writer = serial.Serial(ports[0], 115200) + while len(ports) < 2: + rospy.logwarn('Virtual serial ports not ready yet, waiting...') + rospy.sleep(.5) + + # process all launch files + for launchfile in self.cfg_launches: + rospy.loginfo('========== Testing launch file {} =========='.format(launchfile)) + + # process all logs + for log in self.cfg_logs: + # tcp interface doesn't support binary mode + if self.cfg_launches[launchfile]['interface'] == 'tcp' and self.cfg_logs[log]['mode'] == 'binary': + rospy.loginfo('Skipping log {} for TCP driver'.format(log)) + continue + + rospy.loginfo('---------- Processing logfile {} ----------'.format(log)) + self.errors = [] + self.msg_counter = {} + msg_types = {} + subscribers = [] + + # create subscribers + for topic in self.cfg_logs[log]['topics']: + msg_type = self.cfg_logs[log]['topics'][topic]['_type'] + msg_types[topic] = msg_type + subscribers.append( + rospy.Subscriber( + '/' + topic, + getattr(sys.modules[__name__], msg_type), + self.cb, + [log, topic] + ) + ) + + # launch + if self.cfg_launches[launchfile]['interface'] == 'serial': + p_driver = subprocess.Popen( + [ + 'roslaunch', + 'nmea_navsat_driver', + launchfile, + '--wait' + ] + + self.cfg_logs[log]['parameters'] + + [ + 'port:=' + ports[1], + 'baud:=115200' + ] + ) + elif self.cfg_launches[launchfile]['interface'] == 'tcp': + p_driver = subprocess.Popen( + [ + 'roslaunch', + 'nmea_navsat_driver', + launchfile, + '--wait' + ] + + self.cfg_logs[log]['parameters'] + + [ + 'ip:=' + self.cfg_tester['tcp_host'], + 'port:=' + str(self.tcp_port), + ] + ) + else: + rospy.logerr('Unknown interface: {}'.format(self.cfg_launches[launchfile]['interface'])) + continue + + # wait until driver is running + rospy.sleep(2.) + + # playback log + self.playback_log(launchfile, log) + + # wait until last message is processed, assuming 2 seconds is enough + rospy.sleep(2.) + + # stop driver + p_driver.terminate() + + # let the driver shut down + rospy.sleep(2.) + + # stop subscribers + for subscriber in subscribers: + subscriber.unregister() + + self.print_errors(launchfile, log, msg_types) + + if len(self.errors) > 0: + self.test_failed = True + + self.serial_writer.close() + p_roscore.terminate() + + self.assertFalse(self.test_failed) + + +if __name__ == '__main__': + rostest.rosrun('nmea_navsat_driver', 'driver_tester', TestDriver)