From dfda95c65a351a2aaa72632f3ae927721a8af96b Mon Sep 17 00:00:00 2001 From: Christopher Stender Date: Thu, 28 Jul 2022 12:42:16 +0200 Subject: [PATCH 01/27] prepare changelog for next dev phase --- ChangeLog | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 344802c..aa4a69e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,14 @@ +next +===== +* ... + 0.3.2 ===== +* Seperated doc in overview and details +* Using README as doxygen frontpage, removed duplicated content * Fixed invalid memory access after a client disconnects * Added build support for macOS * Increased CMake requirement to version 3.6 -* Using README as doxygen frontpage, removed duplicated content -* Improved doxygen documentation 0.3.1 ===== From d9e48bad1cb5d79ef452336b4b5f83223d1fd3d2 Mon Sep 17 00:00:00 2001 From: "Vornberger, Katja" Date: Thu, 27 Apr 2023 11:24:01 +0000 Subject: [PATCH 02/27] Resolve "Add MIT license" --- COPYING | 20 ++++++++++++++++++++ ChangeLog | 1 + README.md | 3 +++ doxydir/demo.cpp | 23 ++++++++++++++++++++--- doxydir/test.py | 22 ++++++++++++++++++++++ gitlab-ci/docker-build.sh | 1 + gitlab-ci/docker-upload.sh | 1 + include/creadline.h | 23 ++++++++++++++++++++--- include/japi.h | 23 ++++++++++++++++++++--- include/japi_pushsrv.h | 21 +++++++++++++++++++-- include/japi_utils.h | 23 ++++++++++++++++++++--- include/networking.h | 23 ++++++++++++++++++++--- include/rw_n.h | 23 ++++++++++++++++++++--- package/create_rpm.sh | 1 + src/creadline.c | 23 ++++++++++++++++++++--- src/japi.c | 21 +++++++++++++++++++-- src/japi_intern.h | 21 +++++++++++++++++++-- src/japi_pushsrv.c | 21 +++++++++++++++++++-- src/japi_pushsrv_intern.h | 21 +++++++++++++++++++-- src/japi_utils.c | 21 +++++++++++++++++++-- src/networking.c | 23 ++++++++++++++++++++--- src/prntdbg.h | 21 +++++++++++++++++++-- src/rw_n.c | 23 ++++++++++++++++++++--- test/japi_test.cc | 22 ++++++++++++++++++++++ 24 files changed, 384 insertions(+), 41 deletions(-) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..349a761 --- /dev/null +++ b/COPYING @@ -0,0 +1,20 @@ + +Copyright (c) 2023 Fraunhofer IIS + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ChangeLog b/ChangeLog index aa4a69e..6b18bf1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ next ===== +* Add MIT license * ... 0.3.2 diff --git a/README.md b/README.md index 5fe2e05..94cf978 100644 --- a/README.md +++ b/README.md @@ -182,3 +182,6 @@ You can clone the [demo project](https://git01.iis.fhg.de/ks-ip-lib/software/lib } } +## License + +Copyright (c) 2023 Fraunhofer IIS. Released under the [MIT License](COPYING). diff --git a/doxydir/demo.cpp b/doxydir/demo.cpp index 369d3c0..e73fc6d 100644 --- a/doxydir/demo.cpp +++ b/doxydir/demo.cpp @@ -9,9 +9,26 @@ * \details * This application demonstrates the usage of the JSON API library (libjapi). * - * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + *\copyright + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #include diff --git a/doxydir/test.py b/doxydir/test.py index 51e942f..d8b469d 100755 --- a/doxydir/test.py +++ b/doxydir/test.py @@ -1,5 +1,27 @@ #!/usr/bin/env python3 +# Copyright (c) 2023 Fraunhofer IIS +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the “Software”), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + + +import errno import json import socket import select diff --git a/gitlab-ci/docker-build.sh b/gitlab-ci/docker-build.sh index 6358501..4c5a292 100755 --- a/gitlab-ci/docker-build.sh +++ b/gitlab-ci/docker-build.sh @@ -1,4 +1,5 @@ #!/bin/sh +# Copyright (c) 2023 Fraunhofer IIS set -e diff --git a/gitlab-ci/docker-upload.sh b/gitlab-ci/docker-upload.sh index a4b26c5..80fd0d9 100755 --- a/gitlab-ci/docker-upload.sh +++ b/gitlab-ci/docker-upload.sh @@ -1,4 +1,5 @@ #!/bin/sh +# Copyright (c) 2023 Fraunhofer IIS # see https://git01.iis.fhg.de/ks-ip-lib/software/libjapi/container_registry diff --git a/include/creadline.h b/include/creadline.h index 98fe7a7..a380d0e 100644 --- a/include/creadline.h +++ b/include/creadline.h @@ -10,9 +10,26 @@ * This readline implementation reads a single line from a file descriptor * (e.g. a socket). Two versions are provided. * - * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + *\copyright + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #ifndef __CREADLINE_H__ diff --git a/include/japi.h b/include/japi.h index 3f1ca9c..f2bd023 100644 --- a/include/japi.h +++ b/include/japi.h @@ -9,9 +9,26 @@ * \details * libjapi is a universal JSON API library. * - * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + *\copyright + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #ifndef __JAPI_H__ diff --git a/include/japi_pushsrv.h b/include/japi_pushsrv.h index 0997d7b..2a76606 100644 --- a/include/japi_pushsrv.h +++ b/include/japi_pushsrv.h @@ -10,8 +10,25 @@ * japi_pushsrv is a universal JSON API library. * * \copyright - * Copyright (c) 2019 Fraunhofer IIS. - * All rights reserved. + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #ifndef __JAPI_PUSHSRV_H__ diff --git a/include/japi_utils.h b/include/japi_utils.h index 111e501..38817ff 100644 --- a/include/japi_utils.h +++ b/include/japi_utils.h @@ -9,9 +9,26 @@ * \details * libjapi is a universal JSON API library. * - * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + *\copyright + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #ifndef __JAPI_UTILS_H__ diff --git a/include/networking.h b/include/networking.h index 877b168..4eaca38 100644 --- a/include/networking.h +++ b/include/networking.h @@ -9,9 +9,26 @@ * \details * This module collects networking helper functions like tcp_start_server(). * - * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + *\copyright + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #ifndef __NETWORKING_H__ diff --git a/include/rw_n.h b/include/rw_n.h index 2ce4549..04979e1 100644 --- a/include/rw_n.h +++ b/include/rw_n.h @@ -10,9 +10,26 @@ * Use read_n() to read or write_n() to write a fixed number of bytes from or * to a file descriptor. * - * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + *\copyright + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #ifndef __RW_N_H__ diff --git a/package/create_rpm.sh b/package/create_rpm.sh index 22f07a4..abd93ce 100755 --- a/package/create_rpm.sh +++ b/package/create_rpm.sh @@ -1,4 +1,5 @@ #!/bin/bash +# Copyright (c) 2023 Fraunhofer IIS ###--------------------------------------------------- # 0. change to target directory and cleanup environment diff --git a/src/creadline.c b/src/creadline.c index 430501a..63b6826 100644 --- a/src/creadline.c +++ b/src/creadline.c @@ -10,9 +10,26 @@ * This readline implementation reads a single line from a file descriptor * (e.g. a socket). Two versions are provided. * - * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + *\copyright + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #include diff --git a/src/japi.c b/src/japi.c index bc814d9..f5f7be1 100644 --- a/src/japi.c +++ b/src/japi.c @@ -10,8 +10,25 @@ * libjapi is a universal JSON API library. * * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #include diff --git a/src/japi_intern.h b/src/japi_intern.h index b35fba1..0c6e9d7 100644 --- a/src/japi_intern.h +++ b/src/japi_intern.h @@ -10,8 +10,25 @@ * libjapi is a universal JSON API library. * * \copyright - * Copyright (c) 2019 Fraunhofer IIS. - * All rights reserved. + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #ifndef __JAPI_INTERN_H__ diff --git a/src/japi_pushsrv.c b/src/japi_pushsrv.c index f225e50..feca90f 100644 --- a/src/japi_pushsrv.c +++ b/src/japi_pushsrv.c @@ -10,8 +10,25 @@ * japi_pushsrv is a universal JSON API library. * * \copyright - * Copyright (c) 2019 Fraunhofer IIS. - * All rights reserved. + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #include diff --git a/src/japi_pushsrv_intern.h b/src/japi_pushsrv_intern.h index 5d772d6..2eb5497 100644 --- a/src/japi_pushsrv_intern.h +++ b/src/japi_pushsrv_intern.h @@ -10,8 +10,25 @@ * libjapi is a universal JSON API library. * * \copyright - * Copyright (c) 2019 Fraunhofer IIS. - * All rights reserved. + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #ifndef __JAPI_PUSHSRV_INTERN_H__ diff --git a/src/japi_utils.c b/src/japi_utils.c index 6a31380..9283c7d 100644 --- a/src/japi_utils.c +++ b/src/japi_utils.c @@ -10,8 +10,25 @@ * japi_utils is a universal JSON API helper library. * * \copyright - * Copyright (c) 2019 Fraunhofer IIS. - * All rights reserved. + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #include diff --git a/src/networking.c b/src/networking.c index 43be171..3aa17ce 100644 --- a/src/networking.c +++ b/src/networking.c @@ -9,9 +9,26 @@ * \details * This module collects networking helper functions like tcp_start_server(). * - * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + *\copyright + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #include diff --git a/src/prntdbg.h b/src/prntdbg.h index e0808dc..ae07ec3 100644 --- a/src/prntdbg.h +++ b/src/prntdbg.h @@ -10,8 +10,25 @@ * This module collects macros for debugging. * * \copyright - * Copyright (c) 2019 Fraunhofer IIS. - * All rights reserved. + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ /* diff --git a/src/rw_n.c b/src/rw_n.c index 4d4c227..576cfd3 100644 --- a/src/rw_n.c +++ b/src/rw_n.c @@ -10,9 +10,26 @@ * Use read_n() to read or write_n() to write a fixed number of bytes from or * to a file descriptor. * - * \copyright - * Copyright (c) 2018 Fraunhofer IIS. - * All rights reserved. + *\copyright + * Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ #include diff --git a/test/japi_test.cc b/test/japi_test.cc index 704f23d..1434ca6 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -1,3 +1,25 @@ +/*! +Copyright (c) 2023 Fraunhofer IIS + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the “Software”), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + #include #include From d60addd5ffa61f07784e65d10140fa33919f01ea Mon Sep 17 00:00:00 2001 From: "Vornberger, Katja" Date: Wed, 5 Jul 2023 08:19:48 +0000 Subject: [PATCH 03/27] Resolve "libjapi/doxydir example out of date" --- README.md | 153 ++---------------- doxydir/1_setup.md | 32 ---- doxydir/{3_usage.md => 1_usage.md} | 0 doxydir/2_demo.md | 9 -- doxydir/{8_examples.md => 2_examples.md} | 0 ...oncepts.md => 3_communication_concepts.md} | 0 doxydir/9_references.md | 5 - doxydir/demo.cpp | 56 ++++--- doxydir/test.py | 37 ++--- 9 files changed, 59 insertions(+), 233 deletions(-) delete mode 100644 doxydir/1_setup.md rename doxydir/{3_usage.md => 1_usage.md} (100%) delete mode 100644 doxydir/2_demo.md rename doxydir/{8_examples.md => 2_examples.md} (100%) rename doxydir/{4_communication_concepts.md => 3_communication_concepts.md} (100%) delete mode 100644 doxydir/9_references.md diff --git a/README.md b/README.md index 94cf978..06071a0 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ Prebuild packages can be downloaded [here](http://ks-ip-lib.git01.iis.fhg.de/sof * [cmake version 3.6](https://cmake.org/) ### Installation +Clone the git repository and it's submodules: + + $ git clone --recurse-submodules git@git01.iis.fhg.de:ks-ip-lib/software/libjapi.git + Create a build directory and call *cmake* in that directory. $ mkdir build @@ -41,147 +45,8 @@ You can clone the [demo project](https://git01.iis.fhg.de/ks-ip-lib/software/lib $ git clone --recurse-submodules git@git01.iis.fhg.de:ks-ip-lib/software/libjapi-demo.git -## Usage & Examples -* Create a JAPI context -* Write application specific functions -* Register these application specific functions to libjapi -* Start a JAPI server -* Enjoy the flexibility - -### Server example - - #include - #include - #include - #include /* sleep */ - - #include - #include - #include - - /* User defined push service routine */ - int push_counter(japi_pushsrv_context *psc) - { - json_object *jmsg; - int i; - - assert(psc != NULL); - - i = 0; - jmsg = json_object_new_object(); - - while (psc->enabled) { - /* Create JSON response string */ - json_object_object_add(jmsg,"counter",json_object_new_int(i)); - - /* Push message */ - japi_pushsrv_sendmsg(psc,jmsg); - - i++; - sleep(1); - } - json_object_put(jmsg); - - return 0; - } - - static void rnf_handler(japi_context *ctx, json_object *request, json_object *response) - { - json_object_object_add(response, "japi_response_msg", json_object_new_string("ERROR: No request handler found!")); - } - - static void get_temperature(japi_context *ctx, json_object *request, json_object *response) - { - double temperature; - const char *unit; - - /* - * TODO: Read the temperature from a sensor... - */ - temperature = 27.0; - - /* Provide the temperature in KELVIN (if requested) - * or CELSIUS (default) */ - unit = japi_get_value_as_str(request, "unit"); - if (unit != NULL && strcmp(unit, "kelvin") == 0) { - temperature += 273; - } else { - unit = "celsius"; - } - - /* Prepare and provide response */ - json_object_object_add(response, "temperature", json_object_new_double(temperature)); - json_object_object_add(response, "unit", json_object_new_string(unit)); - } - - int main(int argc, char *argv[]) - { - int ret; - japi_context *ctx; - japi_pushsrv_context *psc_counter; - - /* Read port */ - if (argc != 2) { - fprintf(stderr, "ERROR: Missing argument or wrong amount of arguments.\n" \ - "Usage:\n\t%s \n", argv[0]); - return -1; - } - - /* Create JSON API context */ - ctx = japi_init(NULL); - if (ctx == NULL) { - fprintf(stderr, "ERROR: Failed to create japi context\n"); - return -1; - } - - /* Register JSON API request */ - japi_register_request(ctx, "get_temperature", &get_temperature); - - /* Register push service */ - psc_counter = japi_pushsrv_register(ctx, "push_counter"); - - /* Start push thread */ - japi_pushsrv_start(psc_counter,&push_counter); - - /* Provide JSON API interface via TCP */ - ret = japi_start_server(ctx, argv[1]); - - /* Wait for the thread to finish */ - japi_pushsrv_stop(psc_counter); - - /* Destroy JAPI context */ - japi_destroy(ctx); - - return ret; - } - -### Client JSON request examples - - { - "japi_request": "get_temperature", - "args": { - "unit": "kelvin" - } - } - - { - "japi_request": "japi_pushsrv_list", - } - - { - "japi_request": "japi_pushsrv_subscribe", - "args": { - "service": "push_counter" - } - } - - { - "japi_request": "japi_pushsrv_unsubscribe", - "args": { - "service": "push_counter" - } - } - -## License - -Copyright (c) 2023 Fraunhofer IIS. Released under the [MIT License](COPYING). +## References +* https://github.com/json-c/json-c +* http://json-c.github.io/json-c/ +* https://en.wikipedia.org/wiki/JSON +* https://alan-mushi.github.io/2014/10/28/json-c-tutorial-part-1.html diff --git a/doxydir/1_setup.md b/doxydir/1_setup.md deleted file mode 100644 index 1dbacd9..0000000 --- a/doxydir/1_setup.md +++ /dev/null @@ -1,32 +0,0 @@ - -# Setup - -## Prerequisites -- [json-c](https://github.com/json-c/json-c) -- [cmake version 3.6](https://cmake.org/) - -## Installation - -### Build -Clone the git repository and it's submodules: -\code -git clone git clone --recurse-submodules git@git01.iis.fhg.de:ks-ip-lib/software/libjapi.git -\endcode - -Create a build directory in the libjapi repository and run `cmake`. -\code -cd libjapi -mkdir build/ -cd build/ -cmake ../ -\endcode - -A Makefile is generated. Run `make` to build the libjapi libraries. -\code -make -\endcode -A shared and a static library is built. - -### Packages -Prebuild packages can be found here. - diff --git a/doxydir/3_usage.md b/doxydir/1_usage.md similarity index 100% rename from doxydir/3_usage.md rename to doxydir/1_usage.md diff --git a/doxydir/2_demo.md b/doxydir/2_demo.md deleted file mode 100644 index 3cac733..0000000 --- a/doxydir/2_demo.md +++ /dev/null @@ -1,9 +0,0 @@ - -# Demo -You can clone the demo project, with examples for all features from the repository listed below: - -\code -git clone --recurse-submodules git@git01.iis.fhg.de:ks-ip-lib/software/libjapi-demo.git -\endcode - -Follow the instructions given in libjapi-demo's README.md file. diff --git a/doxydir/8_examples.md b/doxydir/2_examples.md similarity index 100% rename from doxydir/8_examples.md rename to doxydir/2_examples.md diff --git a/doxydir/4_communication_concepts.md b/doxydir/3_communication_concepts.md similarity index 100% rename from doxydir/4_communication_concepts.md rename to doxydir/3_communication_concepts.md diff --git a/doxydir/9_references.md b/doxydir/9_references.md deleted file mode 100644 index b4325b3..0000000 --- a/doxydir/9_references.md +++ /dev/null @@ -1,5 +0,0 @@ -## References -* https://github.com/json-c/json-c -* http://json-c.github.io/json-c/ -* https://en.wikipedia.org/wiki/JSON -* https://alan-mushi.github.io/2014/10/28/json-c-tutorial-part-1.html diff --git a/doxydir/demo.cpp b/doxydir/demo.cpp index e73fc6d..bae61ba 100644 --- a/doxydir/demo.cpp +++ b/doxydir/demo.cpp @@ -37,27 +37,36 @@ #include #include /* sleep */ -#include -#include -#include +#include "japi.h" +#include "japi_pushsrv.h" +#include "japi_utils.h" + +/* Create a resource structure to pass to libjapi*/ +typedef struct resources +{ + double temperature; +}resources; /* * User defined push temperature service routine. * Simulates a circular sinus push value. */ -int push_temperature(japi_pushsrv_context *psc) +void push_temperature(japi_pushsrv_context *psc) { json_object *jmsg; double i; assert(psc != NULL); + /* Get back pointer on resources (i.e for example sensors values) */ + resources* sensor_values = (resources*) psc->userptr; + jmsg = json_object_new_object(); while (psc->enabled) { for (i = 0.0; i <= 3.14; i += 0.1) { /* Create JSON response string */ - json_object_object_add(jmsg,"temperature",json_object_new_double(30+10*sin(i))); + json_object_object_add(jmsg,"temperature",json_object_new_double(sensor_values->temperature+10*sin(i))); /* Push message */ japi_pushsrv_sendmsg(psc,jmsg); @@ -66,12 +75,10 @@ int push_temperature(japi_pushsrv_context *psc) } json_object_put(jmsg); - - return 0; } /* User defined push service routine */ -int push_counter(japi_pushsrv_context *psc) +void push_counter(japi_pushsrv_context *psc) { json_object *jmsg; int i; @@ -92,8 +99,6 @@ int push_counter(japi_pushsrv_context *psc) sleep(1); } json_object_put(jmsg); - - return 0; } static void rnf_handler(japi_context *ctx, json_object *request, json_object *response) @@ -106,14 +111,15 @@ static void get_temperature(japi_context *ctx, json_object *request, json_object double temperature; const char *unit; - /* - * TODO: Read the temperature from a sensor... - */ - temperature = 27.0; + /* Get back pointer on resources (i.e for example sensors values) */ + resources* sensor_values = (resources*) ctx->userptr; + temperature = sensor_values->temperature; /* Provide the temperature in KELVIN (if requested) * or CELSIUS (default) */ - unit = japi_get_value_as_str(request, "unit"); + if (japi_get_value_as_str(request, "unit", &unit) != 0 ) { + fprintf(stderr, "Failed to get string value from key 'unit'\n"); + } if (unit != NULL && strcmp(unit, "kelvin") == 0) { temperature += 273; } else { @@ -122,7 +128,6 @@ static void get_temperature(japi_context *ctx, json_object *request, json_object /* Prepare and provide response */ json_object_object_add(response, "temperature", json_object_new_double(temperature)); - json_object_object_add(response, "unit", json_object_new_string(unit)); } int main(int argc, char *argv[]) @@ -131,6 +136,9 @@ int main(int argc, char *argv[]) japi_context *ctx; japi_pushsrv_context *psc_counter, *psc_temperature; + /* Declare & initialise resources*/ + resources temperature_sensor = {17.0}; + /* Read port */ if (argc != 2) { fprintf(stderr, "ERROR: Missing argument or wrong amount of arguments.\n" \ @@ -139,12 +147,15 @@ int main(int argc, char *argv[]) } /* Create JSON API context */ - ctx = japi_init(NULL); + ctx = japi_init(&temperature_sensor); if (ctx == NULL) { fprintf(stderr, "ERROR: Failed to create japi context\n"); return -1; } + /* Include request args in response */ + japi_include_args_in_response(ctx, true); + /* Register JSON API requests */ japi_register_request(ctx, "request_not_found_handler", &rnf_handler); japi_register_request(ctx, "get_temperature", &get_temperature); @@ -154,16 +165,15 @@ int main(int argc, char *argv[]) psc_temperature = japi_pushsrv_register(ctx, "push_temperature"); /* Start push threads */ - japi_pushsrv_start(psc_counter,&push_counter); - japi_pushsrv_start(psc_temperature,&push_temperature); + japi_pushsrv_start(psc_counter, &push_counter); + japi_pushsrv_start(psc_temperature, &push_temperature); + + /* Set maximal number of allowed clients. 0 for unlimited */ + japi_set_max_allowed_clients(ctx, 3); /* Provide JSON API interface via TCP */ ret = japi_start_server(ctx, argv[1]); - /* Wait for the threads to finish */ - japi_pushsrv_stop(psc_counter); - japi_pushsrv_stop(psc_temperature); - /* Destroy JAPI context */ japi_destroy(ctx); diff --git a/doxydir/test.py b/doxydir/test.py index d8b469d..86c058f 100755 --- a/doxydir/test.py +++ b/doxydir/test.py @@ -32,7 +32,9 @@ def process_request(cmd_request,sock): resp = sock.recv(4096) - print(json.dumps(json.loads(resp.decode("utf-8")), indent=4)) + # Just dump if not empty + if resp: + print(json.dumps(json.loads(resp.decode("utf-8")), indent=4)) def main(): IP = 'localhost' @@ -41,38 +43,32 @@ def main(): try: sock = socket.create_connection((IP, TCP_PORT)) - except (ConnectionError): - log.error("A connection to %s:%s could not be established.", *addr) - return {} - + except socket.error as e: + if e.errno != errno.ECONNREFUSED: + print("Exception was thrown. Message is %s" % (e)) + return 1 + print("A connection to '%s:%d' could not be established." %(IP, TCP_PORT)) + return 1 + cmd_request = { "japi_request": "get_temperature", - "unit": "kelvin", + "args": {"unit": "kelvin"}, } push_service_request = { "japi_request": "japi_pushsrv_list", } push_temperature_request_subscr = { "japi_request": "japi_pushsrv_subscribe", - "service": "push_temperature", + "args": {"service": "push_temperature"}, } push_temperature_request_unsubscr = { "japi_request": "japi_pushsrv_unsubscribe", - "service": "push_temperature", - } - push_counter_request_subscr = { - "japi_request": "japi_pushsrv_subscribe", - "service": "push_counter", - } - push_counter_request_unsubscr = { - "japi_request": "japi_pushsrv_unsubscribe", - "service": "push_counter", + "args": {"service": "push_temperature"}, } process_request(cmd_request,sock) process_request(push_service_request,sock) process_request(push_temperature_request_subscr,sock) - process_request(push_counter_request_subscr,sock) # get push service messages sock.settimeout(10) @@ -80,15 +76,16 @@ def main(): resp = sock.recv(4096) # Iterate line by line for n, line in enumerate(sock.makefile(), start=1): - jdata = json.loads(line) - print(json.dumps(json.loads(line), indent=4)) + # Just dump if received message is not empty + if line: + jdata = json.loads(line) + print(json.dumps(json.loads(line), indent=4)) except: pass finally: pass process_request(push_temperature_request_unsubscr,sock) - process_request(push_counter_request_unsubscr,sock) sock.close() From 8d1c77872f5e54f0c443a182303f6a471c39887a Mon Sep 17 00:00:00 2001 From: "Vornberger, Katja" Date: Wed, 5 Jul 2023 08:26:08 +0000 Subject: [PATCH 04/27] Resolve "Include errors using <>/""" --- include/japi.h | 3 ++- test/japi_test.cc | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/japi.h b/include/japi.h index f2bd023..3aa3cd1 100644 --- a/include/japi.h +++ b/include/japi.h @@ -35,10 +35,11 @@ #define __JAPI_H__ #include -#include #include #include +#include "creadline.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/test/japi_test.cc b/test/japi_test.cc index 1434ca6..abf327d 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -24,12 +24,12 @@ Copyright (c) 2023 Fraunhofer IIS #include extern "C"{ -#include -#include -#include -#include -#include -#include +#include "japi.h" +#include "japi_intern.h" +#include "japi_pushsrv_intern.h" +#include "japi_pushsrv.h" +#include "japi_utils.h" +#include "rw_n.h" } /* The handler for japi_register_request test */ From 8b4420db609c7f25fe9b96637cede7b0b4397747 Mon Sep 17 00:00:00 2001 From: "Vornberger, Katja" Date: Wed, 5 Jul 2023 09:06:07 +0000 Subject: [PATCH 05/27] Resolve "Japi_pushsrv_destroy is not cleaning ctx->push_service element" --- include/japi_pushsrv.h | 16 ++++++++++------ src/japi.c | 2 +- src/japi_pushsrv.c | 35 ++++++++++++++++++++++++++++++----- test/japi_test.cc | 38 +++++++++++++++++++++++++++++++++++--- 4 files changed, 76 insertions(+), 15 deletions(-) diff --git a/include/japi_pushsrv.h b/include/japi_pushsrv.h index 2a76606..a385fda 100644 --- a/include/japi_pushsrv.h +++ b/include/japi_pushsrv.h @@ -81,15 +81,19 @@ typedef struct __japi_pushsrv_context { japi_pushsrv_context* japi_pushsrv_register(japi_context *ctx, const char *pushsrv_name); /*! - * \brief Unsubscribes and frees memory space for all push service clients - * - * Iterates through push service clients and unsubscribes and frees memory. - * - * \param psc JAPI push service context + * \brief Remove push service context from japi context, unsubscribe for all clients and free memory + * + * Clean up the push service if no more needed: + * * remove entry from linked list in `ctx->push_services` + * * unsubscribe all clients and free their memory + * * stop the push service and free the used memory * + * \param ctx JAPI context + * \param psc JAPI push service context which is to be removed + * * \returns On success, 0 is returned. On error, -1 is returned. */ -int japi_pushsrv_destroy(japi_pushsrv_context *psc); +int japi_pushsrv_destroy(japi_context *ctx, japi_pushsrv_context *psc); /*! * \brief Send messages to all subscribed clients diff --git a/src/japi.c b/src/japi.c index f5f7be1..842a779 100644 --- a/src/japi.c +++ b/src/japi.c @@ -221,7 +221,7 @@ int japi_destroy(japi_context *ctx) psc = ctx->push_services; while (psc != NULL) { psc_next = psc->next; - japi_pushsrv_destroy(psc); + japi_pushsrv_destroy(ctx, psc); psc = psc_next; } diff --git a/src/japi_pushsrv.c b/src/japi_pushsrv.c index feca90f..f5febb2 100644 --- a/src/japi_pushsrv.c +++ b/src/japi_pushsrv.c @@ -331,21 +331,46 @@ japi_pushsrv_context* japi_pushsrv_register(japi_context* ctx, const char* pushs return psc; } -/* - * Iterate through push service and unsubscribe & free memory for all clients +/* + * Remove push service context from japi context, unsubscribe for all clients and free memory */ -int japi_pushsrv_destroy(japi_pushsrv_context *psc) +int japi_pushsrv_destroy(japi_context *ctx, japi_pushsrv_context *psc) { + japi_pushsrv_context *psc_iter, *psc_prev, *psc_next; japi_client *client, *client_next; + assert(ctx != NULL); + if (psc == NULL) { - fprintf(stderr,"ERROR: push service context is NULL"); + fprintf(stderr,"ERROR: push service context is NULL\n"); return -1; } + if (strcmp(psc->pushsrv_name, "") == 0) { + fprintf(stderr,"ERROR: push service context is empty\n"); + return -2; + } - client = psc->clients; + /* clean up linked list in ctx->push_service */ + psc_prev = NULL; + psc_iter = ctx->push_services; + + while (psc_iter != NULL) { + psc_next = psc_iter->next; + if (psc_iter == psc) { + /* If first element */ + if (psc_prev == NULL) { + ctx->push_services = psc_next; + } else { + psc_prev->next = psc_next; + } + break; + } + psc_prev = psc_iter; + psc_iter = psc_next; + } /* Iterates through push service client list and frees memory for every element and for the push service themself */ + client = psc->clients; pthread_mutex_lock(&(psc->lock)); while (client != NULL) { client_next = client->next; diff --git a/test/japi_test.cc b/test/japi_test.cc index abf327d..087b0db 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -443,9 +443,41 @@ TEST(JAPI_Push_Service,PushServiceDestroy) psc_status = japi_pushsrv_register(ctx,"pushsrv_temperature"); /* Destroy push services */ - EXPECT_EQ(japi_pushsrv_destroy(psc_status),0); - EXPECT_EQ(japi_pushsrv_destroy(psc_temperature),0); + EXPECT_EQ(japi_pushsrv_destroy(ctx, psc_status),0); + EXPECT_EQ(japi_pushsrv_destroy(ctx, psc_temperature),0); /* Pass bad push service context */ - EXPECT_EQ(japi_pushsrv_destroy(NULL),-1); + EXPECT_EQ(japi_pushsrv_destroy(ctx, NULL),-1); + EXPECT_EQ(japi_pushsrv_destroy(ctx, psc_temperature),-2); +} + +TEST(JAPI_Push_Service,PushServiceRemoveEntryFromLInkedList) +{ + japi_context *ctx; + japi_pushsrv_context *psc01, *psc02, *psc05; + json_object *jobj; + + ctx = japi_init(NULL); + jobj = json_object_new_object(); + + /* Register some test services, creates + { "services": [ "test05", "test04", "test03", "test02", "test01" ] } + */ + psc01 = japi_pushsrv_register(ctx,"test01"); + psc02 = japi_pushsrv_register(ctx,"test02"); + japi_pushsrv_register(ctx,"test03"); + japi_pushsrv_register(ctx,"test04"); + psc05 = japi_pushsrv_register(ctx,"test05"); + + japi_pushsrv_destroy(ctx, psc02); + japi_pushsrv_list(ctx, NULL, jobj); + EXPECT_STREQ(json_object_to_json_string(jobj), "{ \"services\": [ \"test05\", \"test04\", \"test03\", \"test01\" ] }"); + + japi_pushsrv_destroy(ctx, psc05); + japi_pushsrv_list(ctx, NULL, jobj); + EXPECT_STREQ(json_object_to_json_string(jobj), "{ \"services\": [ \"test04\", \"test03\", \"test01\" ] }"); + + japi_pushsrv_destroy(ctx, psc01); + japi_pushsrv_list(ctx, NULL, jobj); + EXPECT_STREQ(json_object_to_json_string(jobj), "{ \"services\": [ \"test04\", \"test03\" ] }"); } From 1dd087d4e4dad3d839b6c735a234760f65c71d6f Mon Sep 17 00:00:00 2001 From: "Vornberger, Katja" Date: Fri, 7 Jul 2023 11:30:08 +0000 Subject: [PATCH 06/27] Resolve "Different behavior of push services in debug and release mode" --- src/japi.c | 7 +++---- src/japi_pushsrv.c | 8 +++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/japi.c b/src/japi.c index 842a779..09d5a65 100644 --- a/src/japi.c +++ b/src/japi.c @@ -94,9 +94,8 @@ int japi_process_message(japi_context *ctx, const char *request, char **response bool args; assert(ctx != NULL); - assert(request != NULL); assert(response != NULL); - assert(socket != -1); + assert(socket >= 0); ret = -1; *response = NULL; @@ -347,7 +346,7 @@ int japi_add_client(japi_context *ctx, int socket) /* Error handling */ assert(ctx != NULL); - assert(socket > 0); + assert(socket >= 0); /* Create new client list element */ client = (japi_client *)malloc(sizeof(japi_client)); @@ -384,7 +383,7 @@ int japi_remove_client(japi_context *ctx, int socket) /* Error Handling */ assert(ctx != NULL); - assert(socket > 0); + assert(socket >= 0); client = ctx->clients; prev = NULL; diff --git a/src/japi_pushsrv.c b/src/japi_pushsrv.c index f5febb2..7792513 100644 --- a/src/japi_pushsrv.c +++ b/src/japi_pushsrv.c @@ -64,7 +64,7 @@ static int japi_pushsrv_add_client(japi_pushsrv_context *psc, int socket) /* Error handling */ assert(psc != NULL); - assert(socket > 0); + assert(socket >= 0); client = (japi_client*)malloc(sizeof(japi_client)); if (client == NULL) { @@ -162,8 +162,7 @@ void japi_pushsrv_subscribe(japi_context *ctx, int socket, json_object *jreq, js /* Error handling */ assert(ctx != NULL); - assert(socket != -1); - assert(jreq != NULL); + assert(socket >= 0); assert(jresp != NULL); psc = ctx->push_services; @@ -207,8 +206,7 @@ void japi_pushsrv_unsubscribe(japi_context *ctx, int socket, json_object *jreq, /* Error handling */ assert(ctx != NULL); - assert(socket != -1); - assert(jreq != NULL); + assert(socket >= 0); assert(jresp != NULL); psc = ctx->push_services; From e784a06e4e7c563f7c14c9f57920804e27c42fff Mon Sep 17 00:00:00 2001 From: Jannis Mainczyk Date: Tue, 11 Jul 2023 14:45:05 +0200 Subject: [PATCH 07/27] add clang-format as pre-commit hook --- .clang-format | 8 ++++++++ .pre-commit-config.yaml | 6 ++++++ README.md | 13 +++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 .clang-format create mode 100644 .pre-commit-config.yaml diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..cf99a98 --- /dev/null +++ b/.clang-format @@ -0,0 +1,8 @@ +BasedOnStyle: LLVM +ColumnLimit: 88 +PointerAlignment: Right +UseTab: Always +IndentWidth: 4 +TabWidth: 4 +BreakBeforeBraces: Linux +AlignTrailingComments: false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c8820ad --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,6 @@ +repos: + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v16.0.6 + hooks: + - id: clang-format + args: [--style, file] diff --git a/README.md b/README.md index 06071a0..42d83a9 100644 --- a/README.md +++ b/README.md @@ -50,3 +50,16 @@ You can clone the [demo project](https://git01.iis.fhg.de/ks-ip-lib/software/lib * http://json-c.github.io/json-c/ * https://en.wikipedia.org/wiki/JSON * https://alan-mushi.github.io/2014/10/28/json-c-tutorial-part-1.html + +## Contributing + +When contributing to this project, automatic formatting of changes is strongly encouraged, so that formatting is getting more consistent over time. + +To do so, ensure you have [`pre-commit`](https://pre-commit.com/) installed and do the following + +```console +$ pre-commit install +pre-commit installed at .git/hooks/pre-commit +``` + +This will run `clang-format` on all *changes* you commit, gradually reformatting the code base to a more consistent state. From 3eb513f43cc38e2df619aa512ba78963d243a5bb Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Tue, 4 Jul 2023 17:36:29 +0200 Subject: [PATCH 08/27] Add and register default function japi_cmd_list --- src/japi.c | 67 +++++++++++++++++++++---------- src/japi_intern.h | 13 ++++++ src/japi_pushsrv.c | 31 +++++++++++---- src/japi_pushsrv_intern.h | 6 +-- test/japi_test.cc | 83 ++++++++++++++++++++++++++++++--------- 5 files changed, 150 insertions(+), 50 deletions(-) diff --git a/src/japi.c b/src/japi.c index 09d5a65..5875878 100644 --- a/src/japi.c +++ b/src/japi.c @@ -138,31 +138,28 @@ int japi_process_message(japi_context *ctx, const char *request, char **response } } - /* Look for subscribe/unsubscribe service and add/remove client socket if found */ - if (strcasecmp(req_name,"japi_pushsrv_subscribe") == 0) { - japi_pushsrv_subscribe(ctx,socket,jargs,jresp_data); - } else if (strcasecmp(req_name,"japi_pushsrv_unsubscribe") == 0) { - japi_pushsrv_unsubscribe(ctx,socket,jargs,jresp_data); - } else { + /* Subscribe/unsubscribe service needs client socket */ + if (strcasecmp(req_name,"japi_pushsrv_subscribe") == 0 || strcasecmp(req_name,"japi_pushsrv_unsubscribe") == 0) { + json_object_object_add(jargs, "socket", json_object_new_int(socket)); + } - /* Try to find a suitable handler for the given request */ - req_handler = japi_get_request_handler(ctx, req_name); - if (req_handler == NULL) { + /* Try to find a suitable handler for the given request */ + req_handler = japi_get_request_handler(ctx, req_name); + if (req_handler == NULL) { - /* No request handler found? Check if a fallback handler was registered. */ - req_handler = japi_get_request_handler(ctx, "request_not_found_handler"); + /* No request handler found? Check if a fallback handler was registered. */ + req_handler = japi_get_request_handler(ctx, "request_not_found_handler"); - if (req_handler == NULL) { - fprintf(stderr, "ERROR: No suitable request handler found. Request was: %s\n", req_name); - goto out_free; - } else { - fprintf(stderr, "WARNING: No suitable request handler found. Falling back to registered fallback handler. Request was: %s\n", req_name); - } + if (req_handler == NULL) { + fprintf(stderr, "ERROR: No suitable request handler found. Request was: %s\n", req_name); + goto out_free; + } else { + fprintf(stderr, "WARNING: No suitable request handler found. Falling back to registered fallback handler. Request was: %s\n", req_name); } - - /* Call request handler */ - req_handler(ctx, jargs, jresp_data); } + + /* Call request handler */ + req_handler(ctx, jargs, jresp_data); } else { /* Get request name */ @@ -298,8 +295,12 @@ japi_context* japi_init(void *userptr) /* Ignore SIGPIPE Signal */ signal(SIGPIPE, SIG_IGN); + /* Register subscribe/unsubscribe service function */ + japi_register_request(ctx, "japi_pushsrv_subscribe", &japi_pushsrv_subscribe); + japi_register_request(ctx, "japi_pushsrv_unsubscribe", &japi_pushsrv_unsubscribe); /* Register list_push_service function */ japi_register_request(ctx, "japi_pushsrv_list", &japi_pushsrv_list); + japi_register_request(ctx, "japi_cmd_list", &japi_cmd_list); return ctx; } @@ -591,3 +592,29 @@ int japi_start_server(japi_context *ctx, const char *port) return 0; } + +/* + * Provide the names of all registered commands as a JAPI response. + */ +void japi_cmd_list(japi_context *ctx, json_object *request, json_object *response) +{ + japi_request *req; + json_object *jstring; + json_object *jarray; + + assert(ctx != NULL); + assert(response != NULL); + + jarray = json_object_new_array(); + req = ctx->requests; + + /* Iterate through push service list and return JSON object */ + while (req != NULL) { + jstring = json_object_new_string(req->name); /* Create JSON-string */ + json_object_array_add(jarray, jstring); /* Add string to JSON array */ + req = req->next; + } + + /* Add array to JSON-object */ + json_object_object_add(response, "commands", jarray); +} diff --git a/src/japi_intern.h b/src/japi_intern.h index 0c6e9d7..1c4ff4c 100644 --- a/src/japi_intern.h +++ b/src/japi_intern.h @@ -115,4 +115,17 @@ int japi_add_client(japi_context *ctx, int socket); */ void japi_pushsrv_remove_client_from_all_pushsrv(japi_context *ctx, int socket); +/*! + * \brief Provide the names of all registered commands as a JAPI response. + * + * Provides the names of all registered commands as a JAPI response. + * + * \param ctx JAPI context + * \param request Pointer to JAPI JSON request + * \param response Pointer to JAPI JSON response + * \note Parameter 'request' declared, although not used in function. + * Function declaration needs to be identical to respective handler. + */ +void japi_cmd_list(japi_context *ctx, json_object *request, json_object *response); + #endif /* __JAPI_INTERN_H__ */ diff --git a/src/japi_pushsrv.c b/src/japi_pushsrv.c index 7792513..c7d2ea2 100644 --- a/src/japi_pushsrv.c +++ b/src/japi_pushsrv.c @@ -154,15 +154,15 @@ void japi_pushsrv_remove_client_from_all_pushsrv(japi_context *ctx, int socket) /* * Saves client socket, if passed push service is registered */ -void japi_pushsrv_subscribe(japi_context *ctx, int socket, json_object *jreq, json_object *jresp) +void japi_pushsrv_subscribe(japi_context *ctx, json_object *jreq, json_object *jresp) { japi_pushsrv_context *psc; json_object *jval; const char* pushsrv_name; + int socket, ret; /* Error handling */ assert(ctx != NULL); - assert(socket >= 0); assert(jresp != NULL); psc = ctx->push_services; @@ -174,11 +174,18 @@ void japi_pushsrv_subscribe(japi_context *ctx, int socket, json_object *jreq, js return; } pushsrv_name = json_object_get_string(jval); + ret = json_object_object_get_ex(jreq,"socket",&jval); + socket = json_object_get_int(jval); + if (!ret | socket < 0) { + json_object_object_add(jresp, "success", json_object_new_boolean(false)); + json_object_object_add(jresp, "message" ,json_object_new_string("Subscribing push service to non-existing socket")); + return; + } /* Search for push service in list and save socket, if found */ while (psc != NULL) { if (strcasecmp(pushsrv_name,psc->pushsrv_name) == 0) { - japi_pushsrv_add_client(psc,socket); + ret = japi_pushsrv_add_client(psc,socket); break; } psc = psc->next; @@ -187,26 +194,26 @@ void japi_pushsrv_subscribe(japi_context *ctx, int socket, json_object *jreq, js json_object_object_add(jresp,"service",json_object_new_string(pushsrv_name)); /* Create JSON response object */ - if (psc != NULL) { - json_object_object_add(jresp,"success",json_object_new_boolean(true)); - } else { + if (psc == NULL | ret < 0) { json_object_object_add(jresp,"success",json_object_new_boolean(false)); json_object_object_add(jresp,"message",json_object_new_string("Push service not found.")); + } else { + json_object_object_add(jresp,"success",json_object_new_boolean(true)); } } /* * Removes client socket, if passed push service is registered */ -void japi_pushsrv_unsubscribe(japi_context *ctx, int socket, json_object *jreq, json_object *jresp) +void japi_pushsrv_unsubscribe(japi_context *ctx, json_object *jreq, json_object *jresp) { japi_pushsrv_context *psc; json_object* jval; const char* pushsrv_name; + int ret, socket; /* Error handling */ assert(ctx != NULL); - assert(socket >= 0); assert(jresp != NULL); psc = ctx->push_services; @@ -221,6 +228,14 @@ void japi_pushsrv_unsubscribe(japi_context *ctx, int socket, json_object *jreq, } pushsrv_name = json_object_get_string(jval); + ret = json_object_object_get_ex(jreq,"socket",&jval); + socket = json_object_get_int(jval); + if (!ret | socket < 0) { + json_object_object_add(jresp, "success", json_object_new_boolean(false)); + json_object_object_add(jresp, "message" ,json_object_new_string("Unsubscribing push service from non-existing socket")); + return; + } + /* Search for push service in list and remove socket, if found & socket is registered */ while (psc != NULL) { if (strcasecmp(pushsrv_name,psc->pushsrv_name) == 0) { diff --git a/src/japi_pushsrv_intern.h b/src/japi_pushsrv_intern.h index 2eb5497..fcdcdcf 100644 --- a/src/japi_pushsrv_intern.h +++ b/src/japi_pushsrv_intern.h @@ -42,11 +42,10 @@ * Subscribe a registered JAPI push service specified by pushsrv_name. * * \param ctx JAPI context - * \param socket Client socket * \param jreq Request JSON object * \param jresp Response JSON object */ -void japi_pushsrv_subscribe(japi_context *ctx, int socket, json_object *jreq, json_object *jresp); +void japi_pushsrv_subscribe(japi_context *ctx, json_object *jreq, json_object *jresp); /*! * \brief Unsubscribe a registered JAPI push service @@ -54,11 +53,10 @@ void japi_pushsrv_subscribe(japi_context *ctx, int socket, json_object *jreq, js * Unsubscribe a registered JAPI push service specified by pushsrv_name. * * \param ctx JAPI context - * \param socket Client socket * \param jreq Request JSON object * \param jresp Response JSON object */ -void japi_pushsrv_unsubscribe(japi_context *ctx, int socket, json_object *jreq, json_object *jresp); +void japi_pushsrv_unsubscribe(japi_context *ctx, json_object *jreq, json_object *jresp); /*! * \brief List registered JAPI push services as JAPI response diff --git a/test/japi_test.cc b/test/japi_test.cc index 087b0db..d8418a1 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -192,6 +192,41 @@ TEST(JAPI,Register) japi_destroy(ctx); } +TEST(JAPI, ListCommands) +{ + japi_context *ctx; + japi_request *cmd; + json_object *jobj; + + ctx = japi_init(NULL); + jobj = json_object_new_object(); + + /* Register some test requests */ + japi_register_request(ctx,"test01",&dummy_request_handler); + japi_register_request(ctx,"test02",&dummy_request_handler); + japi_register_request(ctx,"test03",&dummy_request_handler); + + /* The function to be tested */ + japi_cmd_list(ctx, NULL, jobj); + + cmd = ctx->requests; + + /* Iterate commands array & context and compare strings */ + json_object_object_foreach(jobj, key, val) { + json_object_object_get_ex(jobj, key, &val); + int arraylen = json_object_array_length(val); + int i; + json_object * jvalue; + while (cmd != NULL) { + for (i=0; i< arraylen; i++) { + jvalue = json_object_array_get_idx(val, i); + EXPECT_STREQ(json_object_get_string(jvalue),cmd->name); + cmd = cmd->next; + } + } + } +} + TEST(JAPI_Push_Service,Register) { japi_context *ctx; @@ -231,50 +266,53 @@ TEST(JAPI_Push_Service,SubscribeAndUnsubscribe) /* Build JSON request */ json_object_object_add(jreq,"service",json_object_new_string(pushsrv_name)); + json_object_object_add(jreq, "socket", json_object_new_int(socket)); /* Sub-/unsubscribe before registering, expecting false */ - japi_pushsrv_subscribe(ctx,socket,jreq,jresp); + japi_pushsrv_subscribe(ctx, jreq, jresp); EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); EXPECT_FALSE(bval); - japi_pushsrv_unsubscribe(ctx,socket,jreq,jresp); + japi_pushsrv_unsubscribe(ctx, jreq, jresp); EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); EXPECT_FALSE(bval); /* Pass illegal JSON request, expecting false */ json_object_object_add(illegal_req,"service",NULL); + json_object_object_add(illegal_req, "socket", json_object_new_int(socket)); - japi_pushsrv_subscribe(ctx,socket,illegal_req,jresp); + japi_pushsrv_subscribe(ctx, illegal_req, jresp); EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); EXPECT_FALSE(bval); - japi_pushsrv_unsubscribe(ctx,socket,illegal_req,jresp); + japi_pushsrv_unsubscribe(ctx, illegal_req, jresp); EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); EXPECT_FALSE(bval); /* Pass illegal key */ json_object_object_add(bad_req,"bad_key",json_object_new_string(pushsrv_name)); + json_object_object_add(bad_req, "socket", json_object_new_int(socket)); - japi_pushsrv_subscribe(ctx,socket,bad_req,jresp); + japi_pushsrv_subscribe(ctx, bad_req, jresp); EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); EXPECT_FALSE(bval); - japi_pushsrv_unsubscribe(ctx,socket,bad_req,jresp); + japi_pushsrv_unsubscribe(ctx, bad_req, jresp); EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); EXPECT_FALSE(bval); /* Try to unsubscribe without subscribed before, should fail */ japi_pushsrv_register(ctx,"test_pushsrv"); - japi_pushsrv_unsubscribe(ctx,socket,jreq,jresp); + japi_pushsrv_unsubscribe(ctx, jreq, jresp); EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); EXPECT_FALSE(bval); /* Expect true */ - japi_pushsrv_subscribe(ctx,socket,jreq,jresp); + japi_pushsrv_subscribe(ctx, jreq, jresp); EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); EXPECT_TRUE(bval); - japi_pushsrv_unsubscribe(ctx,socket,jreq,jresp); + japi_pushsrv_unsubscribe(ctx, jreq, jresp); EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); EXPECT_TRUE(bval); @@ -392,14 +430,20 @@ TEST(JAPI_Push_Service,AddRemoveClient) japi_pushsrv_register(ctx,"pushsrv_temperature"); /* Add some clients */ - japi_pushsrv_subscribe(ctx,4,push_temperature_jreq,jobj); - japi_pushsrv_subscribe(ctx,5,push_temperature_jreq,jobj); - japi_pushsrv_subscribe(ctx,6,push_temperature_jreq,jobj); + json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(4)); + japi_pushsrv_subscribe(ctx, push_temperature_jreq, jobj); + json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(5)); + japi_pushsrv_subscribe(ctx, push_temperature_jreq, jobj); + json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(6)); + japi_pushsrv_subscribe(ctx, push_temperature_jreq, jobj); /* Add a clients a second time */ - japi_pushsrv_subscribe(ctx,7,push_temperature_jreq,jobj); - japi_pushsrv_subscribe(ctx,7,push_temperature_jreq,jobj); + json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(7)); + japi_pushsrv_subscribe(ctx, push_temperature_jreq, jobj); + json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(7)); + japi_pushsrv_subscribe(ctx, push_temperature_jreq, jobj); - japi_pushsrv_subscribe(ctx,5,push_status_jreq,jobj); + json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(15)); + japi_pushsrv_subscribe(ctx, push_status_jreq, jobj); counter = 0; psc = ctx->push_services; @@ -412,8 +456,10 @@ TEST(JAPI_Push_Service,AddRemoveClient) EXPECT_EQ(counter,5); /* Unsubscribe some clients */ - japi_pushsrv_unsubscribe(ctx,5,push_temperature_jreq,jobj); - japi_pushsrv_unsubscribe(ctx,6,push_temperature_jreq,jobj); + json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(5)); + japi_pushsrv_unsubscribe(ctx, push_temperature_jreq, jobj); + json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(6)); + japi_pushsrv_unsubscribe(ctx, push_temperature_jreq, jobj); counter = 0; psc = ctx->push_services; @@ -426,7 +472,8 @@ TEST(JAPI_Push_Service,AddRemoveClient) EXPECT_EQ(counter,3); /* Unsubscribe client that is not subscribed */ - japi_pushsrv_unsubscribe(ctx,15,push_temperature_jreq,jobj); + json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(15)); + japi_pushsrv_unsubscribe(ctx, push_temperature_jreq, jobj); EXPECT_EQ(japi_get_value_as_bool(jobj, "success",&bval),0); EXPECT_FALSE(bval); } From f147abba8a30da4437b974310c1ffd3fe0bec845 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Tue, 11 Jul 2023 10:26:59 +0200 Subject: [PATCH 09/27] Disallow user commands from starting with "japi_" --- include/japi.h | 1 + src/japi.c | 9 +++++++++ test/japi_test.cc | 3 +++ 3 files changed, 13 insertions(+) diff --git a/include/japi.h b/include/japi.h index 3aa3cd1..99ac161 100644 --- a/include/japi.h +++ b/include/japi.h @@ -59,6 +59,7 @@ typedef struct __japi_context { struct __japi_client *clients; /*!< Pointer to the JAPI client context */ bool include_args_in_response; /*!< Flag to include request args in response */ bool shutdown; /*!< Flag to shutdown the JAPI server */ + bool init; /*!< Flag to mark finished initialization */ } japi_context; /*! diff --git a/src/japi.c b/src/japi.c index 5875878..a39c5cb 100644 --- a/src/japi.c +++ b/src/japi.c @@ -230,6 +230,7 @@ int japi_destroy(japi_context *ctx) int japi_register_request(japi_context* ctx, const char *req_name, japi_req_handler req_handler) { japi_request *req; + char *bad_req_name = "japi_"; /* Error handling */ if (ctx == NULL) { @@ -252,6 +253,11 @@ int japi_register_request(japi_context* ctx, const char *req_name, japi_req_hand return -4; } + if(ctx->init && strncmp(req_name, bad_req_name, strlen(bad_req_name)) == 0) { + fprintf(stderr, "ERROR: Request name is not allowed.\n"); + return -5; + } + req = (japi_request *)malloc(sizeof(japi_request)); if (req == NULL) { perror("ERROR: malloc() failed"); @@ -277,6 +283,7 @@ japi_context* japi_init(void *userptr) return NULL; } + ctx->init = false; ctx->userptr = userptr; ctx->requests = NULL; ctx->push_services = NULL; @@ -302,6 +309,8 @@ japi_context* japi_init(void *userptr) japi_register_request(ctx, "japi_pushsrv_list", &japi_pushsrv_list); japi_register_request(ctx, "japi_cmd_list", &japi_cmd_list); + ctx->init = true; + return ctx; } diff --git a/test/japi_test.cc b/test/japi_test.cc index d8418a1..8450199 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -189,6 +189,9 @@ TEST(JAPI,Register) EXPECT_EQ(japi_register_request(ctx,"dummy_request_02",&dummy_request_handler),0); // same handler for another name EXPECT_EQ(japi_register_request(ctx,"",&dummy_request_handler),-2); + /* Request names starting with japi_ are not allowed */ + EXPECT_EQ(japi_register_request(ctx,"japi_test",&dummy_request_handler),-5); + japi_destroy(ctx); } From 905a9658a2b2f222035e4dc7ea1f50461a1c7c02 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Tue, 11 Jul 2023 14:14:26 +0200 Subject: [PATCH 10/27] Register default handler avoid crashing if the user did not register one theirself + docs + test --- doxydir/1_usage.md | 7 +++++++ src/japi.c | 16 +++++++++++++--- src/japi_intern.h | 18 ++++++++++++++++++ test/japi_test.cc | 7 +++++++ 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/doxydir/1_usage.md b/doxydir/1_usage.md index 0277474..43db513 100644 --- a/doxydir/1_usage.md +++ b/doxydir/1_usage.md @@ -19,6 +19,8 @@ ctx = japi_init(NULL); japi_register_request(ctx,"function_name",&function_handler); \endcode +Note, that "function_name" may not start with "japi_" which is used to mark internal commands. + The function will be saved in the passed JAPI context. The server which manages the requests, is started with: \code japi_start_server(ctx,8080); @@ -35,3 +37,8 @@ ctx = japi_init(object_pointer); ctx->userptr ... #access to passed argument \endcode +## Default handler +There is a default `japi_request_not_found_handler` which reacts with an error +message if an unknown request is received. If you want to change that behavior, +you can register a `request_not_found_handler` which will then be used instead +to behave as you desire. diff --git a/src/japi.c b/src/japi.c index a39c5cb..e677ef8 100644 --- a/src/japi.c +++ b/src/japi.c @@ -151,10 +151,10 @@ int japi_process_message(japi_context *ctx, const char *request, char **response req_handler = japi_get_request_handler(ctx, "request_not_found_handler"); if (req_handler == NULL) { - fprintf(stderr, "ERROR: No suitable request handler found. Request was: %s\n", req_name); - goto out_free; + fprintf(stderr, "ERROR: No suitable request handler found. Falling back to default fallback handler. Request was: %s\n", req_name); + req_handler = japi_get_request_handler(ctx, "japi_request_not_found_handler"); } else { - fprintf(stderr, "WARNING: No suitable request handler found. Falling back to registered fallback handler. Request was: %s\n", req_name); + fprintf(stderr, "WARNING: No suitable request handler found. Falling back to user registered fallback handler. Request was: %s\n", req_name); } } @@ -302,6 +302,8 @@ japi_context* japi_init(void *userptr) /* Ignore SIGPIPE Signal */ signal(SIGPIPE, SIG_IGN); + /* Register the default fallback handler */ + japi_register_request(ctx, "japi_request_not_found_handler", &japi_request_not_found_handler); /* Register subscribe/unsubscribe service function */ japi_register_request(ctx, "japi_pushsrv_subscribe", &japi_pushsrv_subscribe); japi_register_request(ctx, "japi_pushsrv_unsubscribe", &japi_pushsrv_unsubscribe); @@ -627,3 +629,11 @@ void japi_cmd_list(japi_context *ctx, json_object *request, json_object *respons /* Add array to JSON-object */ json_object_object_add(response, "commands", jarray); } + +/* + * Default handler for reacting to unknown requests. + */ +void japi_request_not_found_handler(japi_context *ctx, json_object *request, json_object *response) +{ + json_object_object_add(response, "error", json_object_new_string("no request handler found")); +} \ No newline at end of file diff --git a/src/japi_intern.h b/src/japi_intern.h index 1c4ff4c..ba6e0d9 100644 --- a/src/japi_intern.h +++ b/src/japi_intern.h @@ -128,4 +128,22 @@ void japi_pushsrv_remove_client_from_all_pushsrv(japi_context *ctx, int socket); */ void japi_cmd_list(japi_context *ctx, json_object *request, json_object *response); +/*! + * \brief Default handler for reacting to unknown requests. + * + * Can be overwritten by the user by registering a command called + * `request_not_found_handler`. + * Is called if no suitable registered request is found for the received command. + * Adds a field named "error" to the response which contains a string describing + * the issue. + * + * \param ctx JAPI context + * \param request Pointer to JAPI JSON request + * \param response Pointer to JAPI JSON response + * \note Parameter 'ctx' and request' declared, although not used in function. + * Function declaration needs to be identical to respective handler. +*/ +void japi_request_not_found_handler( + japi_context *ctx, json_object *request, json_object *response); + #endif /* __JAPI_INTERN_H__ */ diff --git a/test/japi_test.cc b/test/japi_test.cc index 8450199..5f95664 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -124,6 +124,13 @@ TEST(JAPI,ProcessMessage) ctx = japi_init(NULL); socket = 4; + /* If no request is registered, do not die */ + EXPECT_EQ(japi_process_message(ctx, request, &response, socket), 0); + jobj = json_tokener_parse(response); + json_object_object_get_ex(jobj, "data", &jdata); + EXPECT_EQ(japi_get_value_as_str(jdata, "error", &sval), 0); + EXPECT_STREQ("no request handler found", sval); + japi_register_request(ctx,"dummy_request_handler",&dummy_request_handler); /* On success, 0 returned. On error, -1 is returned */ EXPECT_EQ(japi_process_message(ctx, request, &response, socket),0); From 79790e7db883273422e0a7641b5088ac98d225b3 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Tue, 11 Jul 2023 17:39:21 +0200 Subject: [PATCH 11/27] Revert checking for empty push service context in destroy as tests fail --- src/japi_pushsrv.c | 4 ---- test/japi_test.cc | 1 - 2 files changed, 5 deletions(-) diff --git a/src/japi_pushsrv.c b/src/japi_pushsrv.c index c7d2ea2..eef0b4f 100644 --- a/src/japi_pushsrv.c +++ b/src/japi_pushsrv.c @@ -358,10 +358,6 @@ int japi_pushsrv_destroy(japi_context *ctx, japi_pushsrv_context *psc) fprintf(stderr,"ERROR: push service context is NULL\n"); return -1; } - if (strcmp(psc->pushsrv_name, "") == 0) { - fprintf(stderr,"ERROR: push service context is empty\n"); - return -2; - } /* clean up linked list in ctx->push_service */ psc_prev = NULL; diff --git a/test/japi_test.cc b/test/japi_test.cc index 5f95664..41edb6e 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -505,7 +505,6 @@ TEST(JAPI_Push_Service,PushServiceDestroy) /* Pass bad push service context */ EXPECT_EQ(japi_pushsrv_destroy(ctx, NULL),-1); - EXPECT_EQ(japi_pushsrv_destroy(ctx, psc_temperature),-2); } TEST(JAPI_Push_Service,PushServiceRemoveEntryFromLInkedList) From 0dfade630badcdf3955cb98f0115218668b33599 Mon Sep 17 00:00:00 2001 From: vornkat-iis <137415805+vornkat-iis@users.noreply.github.com> Date: Wed, 16 Aug 2023 15:51:46 +0200 Subject: [PATCH 12/27] Update doxydir/1_usage.md Co-authored-by: Jannis Mainczyk --- doxydir/1_usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doxydir/1_usage.md b/doxydir/1_usage.md index 43db513..7528992 100644 --- a/doxydir/1_usage.md +++ b/doxydir/1_usage.md @@ -38,7 +38,7 @@ ctx->userptr ... #access to passed argument \endcode ## Default handler -There is a default `japi_request_not_found_handler` which reacts with an error +There is a default `japi_request_not_found_handler` which responds with an error message if an unknown request is received. If you want to change that behavior, you can register a `request_not_found_handler` which will then be used instead to behave as you desire. From 0e287d2ae4885aa716283b59fe82c6af691a1258 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Wed, 12 Jul 2023 21:57:49 +0200 Subject: [PATCH 13/27] Add github action workflow --- .github/workflows/CMake.yml | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/CMake.yml diff --git a/.github/workflows/CMake.yml b/.github/workflows/CMake.yml new file mode 100644 index 0000000..17550ad --- /dev/null +++ b/.github/workflows/CMake.yml @@ -0,0 +1,44 @@ +name: CMake + +on: + push: + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Dependencies + run: sudo apt install -y libjson-c-dev doxygen graphviz + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: make run_test + + - name: Docs + working-directory: ${{github.workspace}}/build + run: make doc + +# Goal: build_rpm, publish \ No newline at end of file From 387763212af2b6c28ae459e44a1aa6414aea2248 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Thu, 13 Jul 2023 10:59:38 +0200 Subject: [PATCH 14/27] Recommendations for VSCode extensions for CMake --- .vscode/extensions.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..bf7a302 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "matepek.vscode-catch2-test-adapter", + "ms-vscode.cmake-tools", + "ms-vscode.cpptools" + ] +} \ No newline at end of file From 0f21279038cae7bae30b1b6217333d8a5613ece6 Mon Sep 17 00:00:00 2001 From: vornkat-iis <137415805+vornkat-iis@users.noreply.github.com> Date: Fri, 14 Jul 2023 14:45:49 +0200 Subject: [PATCH 15/27] Build RPM packages using centos:7 container --- .github/workflows/CMake.yml | 2 +- .github/workflows/package.yml | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/package.yml diff --git a/.github/workflows/CMake.yml b/.github/workflows/CMake.yml index 17550ad..87d14e2 100644 --- a/.github/workflows/CMake.yml +++ b/.github/workflows/CMake.yml @@ -41,4 +41,4 @@ jobs: working-directory: ${{github.workspace}}/build run: make doc -# Goal: build_rpm, publish \ No newline at end of file +# Goal: publish docs diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml new file mode 100644 index 0000000..e669144 --- /dev/null +++ b/.github/workflows/package.yml @@ -0,0 +1,28 @@ +name: package + +on: + push: + branches: ["master", "add-github-actions"] + +jobs: + package: + runs-on: ubuntu-latest + container: + image: ghcr.io/fraunhofer-iis/libjapi_ci + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v1 + with: + submodules: true + + - name: Build RPM package + run: ls -la; package/create_rpm.sh + + - name: Upload package + uses: actions/upload-artifact@v3 + with: + name: Binary RPM + path: package/rpmbuild/RPMS/x86_64/libjapi*.rpm \ No newline at end of file From b5ec39e5659a92f8fcc74044f8f2a9d9a1e1bdf1 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Sun, 16 Jul 2023 21:04:47 +0200 Subject: [PATCH 16/27] Add actions to publish pages --- .github/workflows/CMake.yml | 6 --- .github/workflows/pages_static.yml | 65 ++++++++++++++++++++++++++++++ README.md | 2 +- 3 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/pages_static.yml diff --git a/.github/workflows/CMake.yml b/.github/workflows/CMake.yml index 87d14e2..be56a39 100644 --- a/.github/workflows/CMake.yml +++ b/.github/workflows/CMake.yml @@ -36,9 +36,3 @@ jobs: # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: make run_test - - - name: Docs - working-directory: ${{github.workspace}}/build - run: make doc - -# Goal: publish docs diff --git a/.github/workflows/pages_static.yml b/.github/workflows/pages_static.yml new file mode 100644 index 0000000..7fb31b6 --- /dev/null +++ b/.github/workflows/pages_static.yml @@ -0,0 +1,65 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + push: + branches: ["master", "add-github-actions"] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + container: + image: ghcr.io/fraunhofer-iis/libjapi_ci + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v1 + with: + submodules: true + + - name: Configure CMake + run: cmake3 -B ${{ env.GITHUB_WORKSPACE }}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Create Docs + working-directory: ${{ env.GITHUB_WORKSPACE }}/build + run: make doc + + - name: Upload artifacts + uses: actions/upload-pages-artifact@v2 + with: + path: '${{ env.GITHUB_WORKSPACE }}/build/doc/html/' + + + deploy: + # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages + permissions: + contents: read + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + needs: build + runs-on: ubuntu-latest + + steps: + - name: Setup Pages + uses: actions/configure-pages@v3 + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 diff --git a/README.md b/README.md index 42d83a9..0e91663 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ create push services, which asynchronously push JSON messages to the clients subscribed to them. ## Documentation -The documentation can be found [here](http://ks-ip-lib.git01.iis.fhg.de/software/libjapi/doc/html/index.html). +The documentation can be found [here](https://fraunhofer-iis.github.io/libjapi/). ## Packages Prebuild packages can be downloaded [here](http://ks-ip-lib.git01.iis.fhg.de/software/libjapi/repo/index.html). From 0ae2e217d765233eb149cad5b3e1d27e9245623d Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Wed, 16 Aug 2023 14:50:40 +0200 Subject: [PATCH 17/27] Use container for CMake action --- .github/workflows/CMake.yml | 20 +++++++++++--------- README.md | 2 +- gitlab-ci/Dockerfile | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/CMake.yml b/.github/workflows/CMake.yml index be56a39..54f8cdc 100644 --- a/.github/workflows/CMake.yml +++ b/.github/workflows/CMake.yml @@ -12,27 +12,29 @@ jobs: # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. # You can convert this to a matrix build if you need cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest + container: + image: ghcr.io/fraunhofer-iis/libjapi_ci + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v1 with: - submodules: recursive - - - name: Install Dependencies - run: sudo apt install -y libjson-c-dev doxygen graphviz + submodules: true - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake3 -B ${{ env.GITHUB_WORKSPACE }}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + run: cmake3 --build ${{ env.GITHUB_WORKSPACE }}/build --config ${{env.BUILD_TYPE}} - name: Test - working-directory: ${{github.workspace}}/build + working-directory: ${{ env.GITHUB_WORKSPACE }}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: make run_test diff --git a/README.md b/README.md index 0e91663..3a34126 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ subscribed to them. The documentation can be found [here](https://fraunhofer-iis.github.io/libjapi/). ## Packages -Prebuild packages can be downloaded [here](http://ks-ip-lib.git01.iis.fhg.de/software/libjapi/repo/index.html). +Prebuild packages for CentOS 7 can be downloaded from the [latest package Action](https://github.com/Fraunhofer-IIS/libjapi/actions/workflows/package.yml). ## Features * Synchronous communication (request, response) diff --git a/gitlab-ci/Dockerfile b/gitlab-ci/Dockerfile index 44c724b..198f1e8 100644 --- a/gitlab-ci/Dockerfile +++ b/gitlab-ci/Dockerfile @@ -2,5 +2,5 @@ FROM centos:7 MAINTAINER Deniz Armagan # Install dependencies -RUN yum install -y epel-release json-c-devel gcc gcc-c++ make git rpm-build doxygen graphviz createrepo -RUN yum install -y cmake3 +RUN yum install -y epel-release json-c-devel gcc gcc-c++ make rpm-build doxygen graphviz createrepo cmake3 + From 0bf33609359be7b3f320aa60d869cea32f064a69 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Wed, 16 Aug 2023 15:46:19 +0200 Subject: [PATCH 18/27] Remove temp pipeline actions --- .github/workflows/package.yml | 2 +- .github/workflows/pages_static.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index e669144..79b5a5a 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -2,7 +2,7 @@ name: package on: push: - branches: ["master", "add-github-actions"] + branches: ["master", "dev"] jobs: package: diff --git a/.github/workflows/pages_static.yml b/.github/workflows/pages_static.yml index 7fb31b6..c1981f5 100644 --- a/.github/workflows/pages_static.yml +++ b/.github/workflows/pages_static.yml @@ -3,7 +3,7 @@ name: Deploy static content to Pages on: push: - branches: ["master", "add-github-actions"] + branches: ["master"] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: From d9f9c35150be667cf19836aa3af4e944a4a51a56 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Wed, 16 Aug 2023 16:34:43 +0200 Subject: [PATCH 19/27] Cleanup old references and files from GitLab --- .gitlab-ci.yml | 94 -------------------------------------- README.md | 2 +- doxydir/2_examples.md | 2 +- gitlab-ci/Dockerfile | 2 - gitlab-ci/docker-build.sh | 22 --------- gitlab-ci/docker-upload.sh | 16 ------- 6 files changed, 2 insertions(+), 136 deletions(-) delete mode 100644 .gitlab-ci.yml delete mode 100755 gitlab-ci/docker-build.sh delete mode 100755 gitlab-ci/docker-upload.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 475c586..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,94 +0,0 @@ -image: git01.iis.fhg.de:5005/ks-ip-lib/software/libjapi:latest - -variables: - GIT_SUBMODULE_STRATEGY: recursive - -stages: - - build - - build_test - - run_test - - doc - - build_rpm - - publish - -job 1: - stage: build - script: - - "mkdir build && cd build" - - "cmake3 ../ 2>&1 | tee cmake.log" - - "make 2>&1 | tee build.log" - artifacts: - when: always - expire_in: 1 week - paths: - - "build/libjapi.so" - - "build/libjapi-static.a" - - "build/cmake.log" - - "build/build.log" -job 2: - stage: build_test - script: - - "cd build" - - "cmake3 ../" - - "make testsuite 2>&1 | tee build_test.log" - artifacts: - when: always - expire_in: 1 week - paths: - - "build/build_test.log" - - "test/*" - - "build/testsuite" -job 3: - stage: run_test - script: - - "cd build" - - "cmake3 ../" - - "make run_test 2>&1 | tee run_test.log" - artifacts: - when: always - expire_in: 1 week - paths: - - "build/run_test.log" -job 4: - stage: doc - script: - - "cd build" - - "cmake3 ../" - - "make doc 2>&1 | tee doc.log" - artifacts: - when: always - expire_in: 1 week - paths: - - "build/doc.log" - - "build/doc/*" -job 5: - stage: build_rpm - script: - - "package/create_rpm.sh" - artifacts: - when: always - expire_in: 1 week - paths: - - "package/rpmbuild/RPMS/x86_64/libjapi*.rpm" -pages: - stage: publish - dependencies: - - job 4 - - job 1 - - job 5 - script: - - mkdir -p public - - mkdir -p public/repo/x86_64 - - mv build/doc public - - mv package/libjapi.repo public/repo/ - - touch public/repo/index.html - - $(echo "

$(basename package/rpmbuild/RPMS/x86_64/libjapi-[0-9]*.rpm)

" >> public/repo/index.html) - - $(echo "

$(basename package/rpmbuild/RPMS/x86_64/libjapi-doc*.rpm)

" >> public/repo/index.html) - - "cp package/rpmbuild/RPMS/x86_64/libjapi*.rpm public/repo/x86_64/" - - createrepo public/repo/ - - yum-config-manager --add-repo public/repo/libjapi.repo - artifacts: - paths: - - public - only: - - master diff --git a/README.md b/README.md index 3a34126..330df19 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Prebuild packages for CentOS 7 can be downloaded from the [latest package Action ### Installation Clone the git repository and it's submodules: - $ git clone --recurse-submodules git@git01.iis.fhg.de:ks-ip-lib/software/libjapi.git + $ git clone --recurse-submodules git@github.com:Fraunhofer-IIS/libjapi.git Create a build directory and call *cmake* in that directory. diff --git a/doxydir/2_examples.md b/doxydir/2_examples.md index 19e20b8..55d3ad2 100644 --- a/doxydir/2_examples.md +++ b/doxydir/2_examples.md @@ -1,6 +1,6 @@ # Examples -To view the full example take a look here. +To view the full example take a look here. \anchor serverExample ## Server example diff --git a/gitlab-ci/Dockerfile b/gitlab-ci/Dockerfile index 198f1e8..293f3a0 100644 --- a/gitlab-ci/Dockerfile +++ b/gitlab-ci/Dockerfile @@ -1,6 +1,4 @@ FROM centos:7 -MAINTAINER Deniz Armagan # Install dependencies RUN yum install -y epel-release json-c-devel gcc gcc-c++ make rpm-build doxygen graphviz createrepo cmake3 - diff --git a/gitlab-ci/docker-build.sh b/gitlab-ci/docker-build.sh deleted file mode 100755 index 4c5a292..0000000 --- a/gitlab-ci/docker-build.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# Copyright (c) 2023 Fraunhofer IIS - -set -e - -# create a temporary directory for the build -BUILDDIR=$(mktemp -d) - -cp Dockerfile $BUILDDIR/ - -echo "Setting up docker build context in $BUILDDIR ..." - -cd $BUILDDIR - -docker build -t libjapi:latest . - -# clean up -cd / - -echo "Build completed successfully. Removing $BUILDDIR ..." -rm -rf "$BUILDDIR" - diff --git a/gitlab-ci/docker-upload.sh b/gitlab-ci/docker-upload.sh deleted file mode 100755 index 80fd0d9..0000000 --- a/gitlab-ci/docker-upload.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -# Copyright (c) 2023 Fraunhofer IIS - -# see https://git01.iis.fhg.de/ks-ip-lib/software/libjapi/container_registry - -set -e # terminate on any error - -LOCAL_IMAGE="libjapi:latest" -REGISTRY="git01.iis.fhg.de:5005" -GITLAB_IMAGE="ks-ip-lib/software/libjapi" - -docker tag $LOCAL_IMAGE $REGISTRY/$GITLAB_IMAGE -docker login $REGISTRY -docker push $REGISTRY/$GITLAB_IMAGE -docker logout $REGISTRY - From c7e01f42c49d3789728bdd717e7c93200730a2b7 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Wed, 16 Aug 2023 18:15:44 +0200 Subject: [PATCH 20/27] Add make target coverage --- CMakeLists.txt | 17 + README.md | 13 + cmake/CodeCoverage.cmake | 742 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 772 insertions(+) create mode 100644 cmake/CodeCoverage.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f9c209d..8809b35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,3 +88,20 @@ target_include_directories(testsuite ) add_custom_target(run_test COMMAND testsuite DEPENDS testsuite) + +################################ +# Test code coverage # + +if(CMAKE_BUILD_TYPE MATCHES "Debug") + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + include(${CMAKE_SOURCE_DIR}/cmake/CodeCoverage.cmake) + set(COVERAGE_EXCLUDES + "doxydir" + "/usr/include/*" + "googletest/*" + "test/*" + ) + append_coverage_compiler_flags() + + setup_target_for_coverage_lcov(NAME coverage EXECUTABLE testsuite) +endif() diff --git a/README.md b/README.md index 330df19..d1ca95f 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ You can clone the [demo project](https://git01.iis.fhg.de/ks-ip-lib/software/lib ## Contributing +### Pre-commit hooks When contributing to this project, automatic formatting of changes is strongly encouraged, so that formatting is getting more consistent over time. To do so, ensure you have [`pre-commit`](https://pre-commit.com/) installed and do the following @@ -63,3 +64,15 @@ pre-commit installed at .git/hooks/pre-commit ``` This will run `clang-format` on all *changes* you commit, gradually reformatting the code base to a more consistent state. + +### Code Coverage +To test which part of the code is called by tests, use the coverage tool. + +To do so, make sure you have lcov installed and do the following + + $ mkdir build + $ cd build/ + $ cmake -DCMAKE_BUILD_TYPE=Debug ../ + $ make coverage + +The result ist displayed in the console. Additionally a report is created at "build/coverage/index.html". diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake new file mode 100644 index 0000000..deb0f6e --- /dev/null +++ b/cmake/CodeCoverage.cmake @@ -0,0 +1,742 @@ +# Copyright (c) 2012 - 2017, Lars Bilke +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. 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. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# 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 HOLDER 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. +# +# CHANGES: +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# 2019-05-06, Anatolii Kurotych +# - Remove unnecessary --coverage flag +# +# 2019-12-13, FeRD (Frank Dana) +# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor +# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. +# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY +# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list +# - Set lcov basedir with -b argument +# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be +# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) +# - Delete output dir, .info file on 'make clean' +# - Remove Python detection, since version mismatches will break gcovr +# - Minor cleanup (lowercase function names, update examples...) +# +# 2019-12-19, FeRD (Frank Dana) +# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets +# +# 2020-01-19, Bob Apthorpe +# - Added gfortran support +# +# 2020-02-17, FeRD (Frank Dana) +# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters +# in EXCLUDEs, and remove manual escaping from gcovr targets +# +# 2021-01-19, Robin Mueller +# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run +# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional +# flags to the gcovr command +# +# 2020-05-04, Mihchael Davis +# - Add -fprofile-abs-path to make gcno files contain absolute paths +# - Fix BASE_DIRECTORY not working when defined +# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines +# +# 2021-05-10, Martin Stump +# - Check if the generator is multi-config before warning about non-Debug builds +# +# 2022-02-22, Marko Wehle +# - Change gcovr output from -o for --xml and --html output respectively. +# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt". +# +# 2022-09-28, Sebastian Mueller +# - fix append_coverage_compiler_flags_to_target to correctly add flags +# - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent) +# +# USAGE: +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt (best inside an if-condition +# using a CMake option() to enable it just optionally): +# include(CodeCoverage) +# +# 3. Append necessary compiler flags for all supported source files: +# append_coverage_compiler_flags() +# Or for specific target: +# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME) +# +# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og +# +# 4. If you need to exclude additional directories from the report, specify them +# using full paths in the COVERAGE_EXCLUDES variable before calling +# setup_target_for_coverage_*(). +# Example: +# set(COVERAGE_EXCLUDES +# '${PROJECT_SOURCE_DIR}/src/dir1/*' +# '/path/to/my/src/dir2/*') +# Or, use the EXCLUDE argument to setup_target_for_coverage_*(). +# Example: +# setup_target_for_coverage_lcov( +# NAME coverage +# EXECUTABLE testrunner +# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") +# +# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set +# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) +# Example: +# set(COVERAGE_EXCLUDES "dir1/*") +# setup_target_for_coverage_gcovr_html( +# NAME coverage +# EXECUTABLE testrunner +# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" +# EXCLUDE "dir2/*") +# +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. +# +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# + +include(CMakeParseArguments) + +option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE) + +# Check prereqs +find_program( GCOV_PATH gcov ) +find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) +find_program( FASTCOV_PATH NAMES fastcov fastcov.py ) +find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) +find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +find_program( CPPFILT_PATH NAMES c++filt ) + +if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") +endif() # NOT GCOV_PATH + +# Check supported compiler (Clang, GNU and Flang) +get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach(LANG ${LANGUAGES}) + if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif() + elseif(NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU" + AND NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(LLVM)?[Ff]lang") + message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...") + endif() +endforeach() + +set(COVERAGE_COMPILER_FLAGS "-g --coverage" + CACHE INTERNAL "") +if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path) + if(HAVE_fprofile_abs_path) + set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") + endif() +endif() + +set(CMAKE_Fortran_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the Fortran compiler during coverage builds." + FORCE ) +set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE ) +set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE ) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE ) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE ) +mark_as_advanced( + CMAKE_Fortran_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) + +get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)) + message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") +endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG) + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) +endif() + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_lcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# ) +function(setup_target_for_coverage_lcov) + + set(options NO_DEMANGLE SONARQUBE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() # NOT LCOV_PATH + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() # NOT GENHTML_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(LCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES LCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Setting up commands which will be run to generate coverage data. + # Cleanup lcov + set(LCOV_CLEAN_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . + -b ${BASEDIR} --zerocounters + ) + # Create baseline to make sure untouched files show up in the report + set(LCOV_BASELINE_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b + ${BASEDIR} -o ${Coverage_NAME}.base + ) + # Run tests + set(LCOV_EXEC_TESTS_CMD + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + ) + # Capturing lcov counters and generating report + set(LCOV_CAPTURE_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b + ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture + ) + # add baseline counters + set(LCOV_BASELINE_COUNT_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base + -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total + ) + # filter collected data to final coverage report + set(LCOV_FILTER_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove + ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info + ) + # Generate HTML output + set(LCOV_GEN_HTML_CMD + ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o + ${Coverage_NAME} ${Coverage_NAME}.info + ) + if(${Coverage_SONARQUBE}) + # Generate SonarQube output + set(GCOVR_XML_CMD + ${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + set(GCOVR_XML_CMD_COMMAND + COMMAND ${GCOVR_XML_CMD} + ) + set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml) + set(GCOVR_XML_CMD_COMMENT COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.") + endif() + + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + message(STATUS "Command to clean up lcov: ") + string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}") + message(STATUS "${LCOV_CLEAN_CMD_SPACED}") + + message(STATUS "Command to create baseline: ") + string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}") + message(STATUS "${LCOV_BASELINE_CMD_SPACED}") + + message(STATUS "Command to run the tests: ") + string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}") + message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to capture counters and generate report: ") + string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}") + message(STATUS "${LCOV_CAPTURE_CMD_SPACED}") + + message(STATUS "Command to add baseline counters: ") + string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}") + message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}") + + message(STATUS "Command to filter collected data: ") + string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}") + message(STATUS "${LCOV_FILTER_CMD_SPACED}") + + message(STATUS "Command to generate lcov HTML output: ") + string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}") + message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}") + + if(${Coverage_SONARQUBE}) + message(STATUS "Command to generate SonarQube XML output: ") + string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") + message(STATUS "${GCOVR_XML_CMD_SPACED}") + endif() + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + COMMAND ${LCOV_CLEAN_CMD} + COMMAND ${LCOV_BASELINE_CMD} + COMMAND ${LCOV_EXEC_TESTS_CMD} + COMMAND ${LCOV_CAPTURE_CMD} + COMMAND ${LCOV_BASELINE_COUNT_CMD} + COMMAND ${LCOV_FILTER_CMD} + COMMAND ${LCOV_GEN_HTML_CMD} + ${GCOVR_XML_CMD_COMMAND} + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.base + ${Coverage_NAME}.capture + ${Coverage_NAME}.total + ${Coverage_NAME}.info + ${GCOVR_XML_CMD_BYPRODUCTS} + ${Coverage_NAME}/index.html + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ${GCOVR_XML_CMD_COMMENT} + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_lcov + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_xml( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the +# GCVOR command. +function(setup_target_for_coverage_gcovr_xml) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + # Set up commands which will be run to generate coverage data + # Run tests + set(GCOVR_XML_EXEC_TESTS_CMD + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + ) + # Running gcovr + set(GCOVR_XML_CMD + ${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + + message(STATUS "Command to run tests: ") + string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}") + message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to generate gcovr XML coverage data: ") + string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") + message(STATUS "${GCOVR_XML_CMD_SPACED}") + endif() + + add_custom_target(${Coverage_NAME} + COMMAND ${GCOVR_XML_EXEC_TESTS_CMD} + COMMAND ${GCOVR_XML_CMD} + + BYPRODUCTS ${Coverage_NAME}.xml + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." + ) +endfunction() # setup_target_for_coverage_gcovr_xml + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_html( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the +# GCVOR command. +function(setup_target_for_coverage_gcovr_html) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + # Set up commands which will be run to generate coverage data + # Run tests + set(GCOVR_HTML_EXEC_TESTS_CMD + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + ) + # Create folder + set(GCOVR_HTML_FOLDER_CMD + ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} + ) + # Running gcovr + set(GCOVR_HTML_CMD + ${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + + message(STATUS "Command to run tests: ") + string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}") + message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to create a folder: ") + string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}") + message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}") + + message(STATUS "Command to generate gcovr HTML coverage data: ") + string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}") + message(STATUS "${GCOVR_HTML_CMD_SPACED}") + endif() + + add_custom_target(${Coverage_NAME} + COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD} + COMMAND ${GCOVR_HTML_FOLDER_CMD} + COMMAND ${GCOVR_HTML_CMD} + + BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce HTML code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_gcovr_html + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_fastcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude. +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# SKIP_HTML # Don't create html report +# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths +# ) +function(setup_target_for_coverage_fastcov) + + set(options NO_DEMANGLE SKIP_HTML) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT FASTCOV_PATH) + message(FATAL_ERROR "fastcov not found! Aborting...") + endif() + + if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (Patterns, not paths, for fastcov) + set(FASTCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES}) + list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES FASTCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Set up commands which will be run to generate coverage data + set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) + + set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} + --search-directory ${BASEDIR} + --process-gcno + --output ${Coverage_NAME}.json + --exclude ${FASTCOV_EXCLUDES} + ) + + set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH} + -C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info + ) + + if(Coverage_SKIP_HTML) + set(FASTCOV_HTML_CMD ";") + else() + set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} + -o ${Coverage_NAME} ${Coverage_NAME}.info + ) + endif() + + set(FASTCOV_POST_CMD ";") + if(Coverage_POST_CMD) + set(FASTCOV_POST_CMD ${Coverage_POST_CMD}) + endif() + + if(CODE_COVERAGE_VERBOSE) + message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):") + + message(" Running tests:") + string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}") + message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}") + + message(" Capturing fastcov counters and generating report:") + string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}") + message(" ${FASTCOV_CAPTURE_CMD_SPACED}") + + message(" Converting fastcov .json to lcov .info:") + string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}") + message(" ${FASTCOV_CONVERT_CMD_SPACED}") + + if(NOT Coverage_SKIP_HTML) + message(" Generating HTML report: ") + string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}") + message(" ${FASTCOV_HTML_CMD_SPACED}") + endif() + if(Coverage_POST_CMD) + message(" Running post command: ") + string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}") + message(" ${FASTCOV_POST_CMD_SPACED}") + endif() + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + + # Cleanup fastcov + COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} + --search-directory ${BASEDIR} + --zerocounters + + COMMAND ${FASTCOV_EXEC_TESTS_CMD} + COMMAND ${FASTCOV_CAPTURE_CMD} + COMMAND ${FASTCOV_CONVERT_CMD} + COMMAND ${FASTCOV_HTML_CMD} + COMMAND ${FASTCOV_POST_CMD} + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.info + ${Coverage_NAME}.json + ${Coverage_NAME}/index.html # report directory + + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report." + ) + + set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.") + if(NOT Coverage_SKIP_HTML) + string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.") + endif() + # Show where to find the fastcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG} + ) + +endfunction() # setup_target_for_coverage_fastcov + +function(append_coverage_compiler_flags) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") +endfunction() # append_coverage_compiler_flags + +# Setup coverage for specific library +function(append_coverage_compiler_flags_to_target name) + separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}") + target_compile_options(${name} PRIVATE ${_flag_list}) + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + target_link_libraries(${name} PRIVATE gcov) + endif() +endfunction() \ No newline at end of file From a6dec8ce244d65d5a0c8e782fb77aa0044188ac3 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Thu, 17 Aug 2023 18:33:35 +0200 Subject: [PATCH 21/27] Add code coverage report to Pages --- .github/workflows/pages_static.yml | 11 ++++++++++- README.md | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pages_static.yml b/.github/workflows/pages_static.yml index c1981f5..2b22661 100644 --- a/.github/workflows/pages_static.yml +++ b/.github/workflows/pages_static.yml @@ -9,7 +9,7 @@ on: env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - BUILD_TYPE: Release + BUILD_TYPE: Debug # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. @@ -31,6 +31,9 @@ jobs: with: submodules: true + - name: Install debug dependencies + run: yum -y install lcov + - name: Configure CMake run: cmake3 -B ${{ env.GITHUB_WORKSPACE }}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} @@ -38,6 +41,12 @@ jobs: working-directory: ${{ env.GITHUB_WORKSPACE }}/build run: make doc + - name: Create Code Coverage Report + working-directory: ${{ env.GITHUB_WORKSPACE }}/build + run: | + make coverage && + cp -r coverage doc/html + - name: Upload artifacts uses: actions/upload-pages-artifact@v2 with: diff --git a/README.md b/README.md index d1ca95f..58d3c50 100644 --- a/README.md +++ b/README.md @@ -76,3 +76,4 @@ To do so, make sure you have lcov installed and do the following $ make coverage The result ist displayed in the console. Additionally a report is created at "build/coverage/index.html". +You can also find it [here](https://fraunhofer-iis.github.io/libjapi/coverage/index.html). From a9f32451c8f5b218ab2f77bf9bde3eda78fcc793 Mon Sep 17 00:00:00 2001 From: vornkat-iis <137415805+vornkat-iis@users.noreply.github.com> Date: Mon, 21 Aug 2023 11:48:52 +0200 Subject: [PATCH 22/27] Update doxydir/2_examples.md Co-authored-by: Jannis Mainczyk --- doxydir/2_examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doxydir/2_examples.md b/doxydir/2_examples.md index 55d3ad2..58f27b4 100644 --- a/doxydir/2_examples.md +++ b/doxydir/2_examples.md @@ -1,6 +1,6 @@ # Examples -To view the full example take a look here. +To view the full example take a look [here](https://github.com/Fraunhofer-IIS/libjapi/blob/master/doxydir/demo.cpp). \anchor serverExample ## Server example From dc4cbb84eb5d20d0d433a0104319cb38d537d392 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Mon, 21 Aug 2023 11:54:22 +0200 Subject: [PATCH 23/27] Prepare next release --- ChangeLog | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6b18bf1..1ccb84a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,15 @@ next ===== -* Add MIT license -* ... +* + +0.4.0 +===== +* Add MIT license and publish to GitHub +* Add make target coverage +* Add githook for formatting with clang +* Move GitLab-CI to GitHub actions +* Add japi handler that lists registered commands +* Some fixes and doc update 0.3.2 ===== From 65561c21ef5ff5c21f1350e309df80660214d838 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Fri, 19 Jan 2024 10:55:24 +0100 Subject: [PATCH 24/27] clang run fir the first time changes would have hidden small review adjustments --- include/japi.h | 30 +++++--- src/japi.c | 140 +++++++++++++++++++--------------- src/japi_pushsrv.c | 183 ++++++++++++++++++++++++++------------------- 3 files changed, 206 insertions(+), 147 deletions(-) diff --git a/include/japi.h b/include/japi.h index 99ac161..f5df23a 100644 --- a/include/japi.h +++ b/include/japi.h @@ -55,7 +55,8 @@ typedef struct __japi_context { uint16_t max_clients; /*!< Number of maximal allowed clients */ pthread_mutex_t lock; /*!< Mutual access lock */ struct __japi_request *requests; /*!< Pointer to the JAPI request list */ - struct __japi_pushsrv_context *push_services; /*!< Pointer to the JAPI push service list */ + struct __japi_pushsrv_context + *push_services; /*!< Pointer to the JAPI push service list */ struct __japi_client *clients; /*!< Pointer to the JAPI client context */ bool include_args_in_response; /*!< Flag to include request args in response */ bool shutdown; /*!< Flag to shutdown the JAPI server */ @@ -70,23 +71,25 @@ typedef struct __japi_context { typedef struct __japi_client { int socket; /*!< Socket to connect */ creadline_buf_t crl_buffer; /*!< Buffer used by creadline_r() */ - struct __japi_client* next; /*!< Pointer to the next client struct or NULL */ + struct __japi_client *next; /*!< Pointer to the next client struct or NULL */ } japi_client; /*! * \brief JAPI request handler type. */ -typedef void (*japi_req_handler)(japi_context *ctx, json_object *request, json_object *response); +typedef void (*japi_req_handler)(japi_context *ctx, json_object *request, + json_object *response); /*! * \brief JAPI request struct. * - * A JAPI request struct is a mapping between a unique request name and a JAPI request handler. + * A JAPI request struct is a mapping between a unique request name and a JAPI request + * handler. */ typedef struct __japi_request { const char *name; /*!< Printable name of the request */ japi_req_handler func; /*!< Function to call */ - struct __japi_request* next; /*!< Pointer to the next request struct or NULL */ + struct __japi_request *next; /*!< Pointer to the next request struct or NULL */ } japi_request; /*! @@ -99,7 +102,7 @@ typedef struct __japi_request { * * \returns On success, a japi_context object is returned. On error, NULL is returned. */ -japi_context* japi_init(void *userptr); +japi_context *japi_init(void *userptr); /*! * \brief Destroy a JAPI context. @@ -122,13 +125,15 @@ int japi_destroy(japi_context *ctx); * \param req_name Request name * \param req_handler Function pointer * - * \returns On success, zero is returned. On error, -1 for empty JAPI context, + * \returns On success, zero is returned. On error, + * -1 for empty JAPI context, * -2 for empty request name, * -3 for empty request handler, * -4 for duplicate naming, * -5 for failed memory allocation, is returned. */ -int japi_register_request(japi_context *ctx, const char *req_name, japi_req_handler req_handler); +int japi_register_request(japi_context *ctx, const char *req_name, + japi_req_handler req_handler); /*! * \brief Start a JAPI server @@ -150,7 +155,8 @@ int japi_start_server(japi_context *ctx, const char *port); * \param ctx JAPI context * \param num Number of clients to be allowed. 0 stands for unlimited. * - * \returns On success, zero is returned. On error, -1 for empty JAPI context, is returned. + * \returns On success, zero is returned. On error, -1 for empty JAPI context, is + * returned. */ int japi_set_max_allowed_clients(japi_context *ctx, uint16_t num); @@ -162,7 +168,8 @@ int japi_set_max_allowed_clients(japi_context *ctx, uint16_t num); * \param ctx JAPI context * \param include_args Include request arguments in response. * - * \returns On success, zero is returned. On error, -1 for empty JAPI context, is returned. + * \returns On success, zero is returned. On error, -1 for empty JAPI context, is + * returned. */ int japi_include_args_in_response(japi_context *ctx, bool include_args); @@ -173,7 +180,8 @@ int japi_include_args_in_response(japi_context *ctx, bool include_args); * * \param ctx JAPI context * - * \returns On success, zero is returned. On error, -1 for empty JAPI context, is returned. + * \returns On success, zero is returned. On error, -1 for empty JAPI context, is + * returned. */ int japi_shutdown(japi_context *ctx); diff --git a/src/japi.c b/src/japi.c index e677ef8..fdfa87f 100644 --- a/src/japi.c +++ b/src/japi.c @@ -34,10 +34,10 @@ #include #include #include -#include /* strcasecmp */ #include /* strcmp */ -#include +#include /* strcasecmp */ #include +#include #include #include @@ -45,13 +45,12 @@ #include "creadline.h" #include "japi_intern.h" -#include "japi_pushsrv_intern.h" #include "japi_pushsrv.h" +#include "japi_pushsrv_intern.h" #include "japi_utils.h" -#include "rw_n.h" #include "networking.h" #include "prntdbg.h" - +#include "rw_n.h" /* Look for a request handler matching the name 'name'. * @@ -81,9 +80,10 @@ static japi_req_handler japi_get_request_handler(japi_context *ctx, const char * * - Prepare the JSON response * - Free memory */ -int japi_process_message(japi_context *ctx, const char *request, char **response, int socket) +int japi_process_message(japi_context *ctx, const char *request, char **response, + int socket) { - const char* req_name; + const char *req_name; json_object *jreq; json_object *jreq_no; json_object *jresp; @@ -103,20 +103,21 @@ int japi_process_message(japi_context *ctx, const char *request, char **response /* Create JSON object from received message */ jreq = json_tokener_parse(request); if (jreq == NULL) { - fprintf(stderr, "ERROR: json_tokener_parse() failed. Received message: %s\n", request); + fprintf(stderr, "ERROR: json_tokener_parse() failed. Received message: %s\n", + request); return -1; } - + /* Only create new JSON objects after a valid JSON request was parsed. */ jresp = json_object_new_object(); /* Response object */ jresp_data = json_object_new_object(); - if ((japi_get_value_as_str(jreq, "japi_request", &req_name)) == 0) { /* Prepare response */ - json_object_object_add(jresp, "japi_response", json_object_new_string(req_name)); - + json_object_object_add(jresp, "japi_response", + json_object_new_string(req_name)); + /* Include japi_request_no in response, if included with request */ if (json_object_object_get_ex(jreq, "japi_request_no", &jreq_no)) { json_object_get(jreq_no); @@ -125,11 +126,11 @@ int japi_process_message(japi_context *ctx, const char *request, char **response /* Get arguments as an JSON object */ args = json_object_object_get_ex(jreq, "args", &jargs); - + /* Add an empty args JSON object if no args were given Otherwise, include args with response, if configured. */ if (!args) { - json_object_object_add(jreq,"args",NULL); + json_object_object_add(jreq, "args", NULL); json_object_object_get_ex(jreq, "args", &jargs); } else { if (ctx->include_args_in_response) { @@ -139,7 +140,8 @@ int japi_process_message(japi_context *ctx, const char *request, char **response } /* Subscribe/unsubscribe service needs client socket */ - if (strcasecmp(req_name,"japi_pushsrv_subscribe") == 0 || strcasecmp(req_name,"japi_pushsrv_unsubscribe") == 0) { + if (strcasecmp(req_name, "japi_pushsrv_subscribe") == 0 || + strcasecmp(req_name, "japi_pushsrv_unsubscribe") == 0) { json_object_object_add(jargs, "socket", json_object_new_int(socket)); } @@ -151,13 +153,20 @@ int japi_process_message(japi_context *ctx, const char *request, char **response req_handler = japi_get_request_handler(ctx, "request_not_found_handler"); if (req_handler == NULL) { - fprintf(stderr, "ERROR: No suitable request handler found. Falling back to default fallback handler. Request was: %s\n", req_name); - req_handler = japi_get_request_handler(ctx, "japi_request_not_found_handler"); + fprintf(stderr, + "ERROR: No suitable request handler found. Falling back to " + "default fallback handler. Request was: %s\n", + req_name); + req_handler = + japi_get_request_handler(ctx, "japi_request_not_found_handler"); } else { - fprintf(stderr, "WARNING: No suitable request handler found. Falling back to user registered fallback handler. Request was: %s\n", req_name); + fprintf(stderr, + "WARNING: No suitable request handler found. Falling back to " + "user registered fallback handler. Request was: %s\n", + req_name); } } - + /* Call request handler */ req_handler(ctx, jargs, jresp_data); @@ -194,7 +203,7 @@ int japi_shutdown(japi_context *ctx) ctx->shutdown = true; - return 0; + return 0; } int japi_destroy(japi_context *ctx) @@ -227,7 +236,8 @@ int japi_destroy(japi_context *ctx) return 0; } -int japi_register_request(japi_context* ctx, const char *req_name, japi_req_handler req_handler) +int japi_register_request(japi_context *ctx, const char *req_name, + japi_req_handler req_handler) { japi_request *req; char *bad_req_name = "japi_"; @@ -238,7 +248,7 @@ int japi_register_request(japi_context* ctx, const char *req_name, japi_req_hand return -1; } - if ((req_name == NULL) || (strcmp(req_name,"") == 0)) { + if ((req_name == NULL) || (strcmp(req_name, "") == 0)) { fprintf(stderr, "ERROR: Request name is NULL or empty.\n"); return -2; } @@ -248,12 +258,14 @@ int japi_register_request(japi_context* ctx, const char *req_name, japi_req_hand return -3; } - if (japi_get_request_handler(ctx,req_name) != NULL) { - fprintf(stderr,"ERROR: A request handler called '%s' was already registered.\n",req_name); + if (japi_get_request_handler(ctx, req_name) != NULL) { + fprintf(stderr, + "ERROR: A request handler called '%s' was already registered.\n", + req_name); return -4; } - if(ctx->init && strncmp(req_name, bad_req_name, strlen(bad_req_name)) == 0) { + if (ctx->init && strncmp(req_name, bad_req_name, strlen(bad_req_name)) == 0) { fprintf(stderr, "ERROR: Request name is not allowed.\n"); return -5; } @@ -273,7 +285,7 @@ int japi_register_request(japi_context* ctx, const char *req_name, japi_req_hand return 0; } -japi_context* japi_init(void *userptr) +japi_context *japi_init(void *userptr) { japi_context *ctx; @@ -294,16 +306,17 @@ japi_context* japi_init(void *userptr) ctx->shutdown = false; /* Initialize mutex */ - if (pthread_mutex_init(&(ctx->lock),NULL) != 0) { - fprintf(stderr,"ERROR: mutex initialization has failed\n"); + if (pthread_mutex_init(&(ctx->lock), NULL) != 0) { + fprintf(stderr, "ERROR: mutex initialization has failed\n"); return NULL; } - /* Ignore SIGPIPE Signal */ + /* Ignore SIGPIPE Signal */ signal(SIGPIPE, SIG_IGN); /* Register the default fallback handler */ - japi_register_request(ctx, "japi_request_not_found_handler", &japi_request_not_found_handler); + japi_register_request(ctx, "japi_request_not_found_handler", + &japi_request_not_found_handler); /* Register subscribe/unsubscribe service function */ japi_register_request(ctx, "japi_pushsrv_subscribe", &japi_pushsrv_subscribe); japi_register_request(ctx, "japi_pushsrv_unsubscribe", &japi_pushsrv_unsubscribe); @@ -324,10 +337,10 @@ int japi_set_max_allowed_clients(japi_context *ctx, uint16_t num) { /* Error handling */ if (ctx == NULL) { - fprintf(stderr, "ERROR: JAPI context is NULL.\n"); - return -1; - } - + fprintf(stderr, "ERROR: JAPI context is NULL.\n"); + return -1; + } + ctx->max_clients = num; return 0; @@ -343,7 +356,7 @@ int japi_include_args_in_response(japi_context *ctx, bool include_args) fprintf(stderr, "ERROR: JAPI context is NULL.\n"); return -1; } - + ctx->include_args_in_response = include_args; return 0; @@ -371,7 +384,7 @@ int japi_add_client(japi_context *ctx, int socket) client->crl_buffer.nbytes = 0; pthread_mutex_lock(&(ctx->lock)); - prntdbg("adding client %d to japi context\n",socket); + prntdbg("adding client %d to japi context\n", socket); /* Add socket */ client->socket = socket; @@ -401,7 +414,7 @@ int japi_remove_client(japi_context *ctx, int socket) prev = NULL; ret = -1; - japi_pushsrv_remove_client_from_all_pushsrv(ctx,socket); + japi_pushsrv_remove_client_from_all_pushsrv(ctx, socket); pthread_mutex_lock(&(ctx->lock)); /* Remove client from list */ @@ -409,7 +422,8 @@ int japi_remove_client(japi_context *ctx, int socket) /* If first element */ if ((client->socket == socket) && (prev == NULL)) { ctx->clients = client->next; - prntdbg("removing client %d from japi context and close socket\n",client->socket); + prntdbg("removing client %d from japi context and close socket\n", + client->socket); close(client->socket); free(client); ctx->num_clients--; @@ -419,7 +433,8 @@ int japi_remove_client(japi_context *ctx, int socket) /* If last element */ if ((client->socket == socket) && (client->next == NULL)) { prev->next = NULL; - prntdbg("removing client %d from japi context and close socket\n",client->socket); + prntdbg("removing client %d from japi context and close socket\n", + client->socket); close(client->socket); free(client); ctx->num_clients--; @@ -428,7 +443,8 @@ int japi_remove_client(japi_context *ctx, int socket) } if (client->socket == socket) { prev->next = client->next; - prntdbg("removing client %d from japi context and close socket\n",client->socket); + prntdbg("removing client %d from japi context and close socket\n", + client->socket); close(client->socket); free(client); ctx->num_clients--; @@ -444,17 +460,17 @@ int japi_remove_client(japi_context *ctx, int socket) return ret; } -int japi_remove_all_clients(japi_context *ctx) +int japi_remove_all_clients(japi_context *ctx) { japi_client *client, *following_client; /* Error Handling */ assert(ctx != NULL); - + client = ctx->clients; while (client != NULL) { following_client = client->next; - if (japi_remove_client(ctx,client->socket) != 0) { + if (japi_remove_client(ctx, client->socket) != 0) { return -1; } client = following_client; @@ -528,12 +544,13 @@ int japi_start_server(japi_context *ctx, const char *port) if (FD_ISSET(client->socket, &fdrd)) { int ret; - char* request; - char* response; + char *request; + char *response; do { - ret = creadline_r(client->socket, (void**)&request, &(client->crl_buffer)); + ret = creadline_r(client->socket, (void **)&request, + &(client->crl_buffer)); if (ret > 0) { response = NULL; @@ -543,8 +560,8 @@ int japi_start_server(japi_context *ctx, const char *port) /* After the request buffer is processed, the memory *is not needed anymore and can be freed at this point. */ - free(request); - + free(request); + /* Send response (if provided) */ if (response != NULL) { ret = write_n(client->socket, response, strlen(response)); @@ -552,16 +569,19 @@ int japi_start_server(japi_context *ctx, const char *port) if (ret <= 0) { /* Write failed */ - fprintf(stderr, "ERROR: Failed to send response to client %i (write returned %i)\n",client->socket, ret); - japi_remove_client(ctx,client->socket); + fprintf(stderr, + "ERROR: Failed to send response to client %i " + "(write returned %i)\n", + client->socket, ret); + japi_remove_client(ctx, client->socket); break; } } } else if (ret == 0) { - if(request == NULL) { + if (request == NULL) { /* Received EOF (client disconnected) */ - prntdbg("client %d disconnected\n",client->socket); - japi_remove_client(ctx,client->socket); + prntdbg("client %d disconnected\n", client->socket); + japi_remove_client(ctx, client->socket); break; } else { /* Received an empty line */ @@ -569,7 +589,7 @@ int japi_start_server(japi_context *ctx, const char *port) } } else { fprintf(stderr, "ERROR: creadline() failed (ret = %i)\n", ret); - japi_remove_client(ctx,client->socket); + japi_remove_client(ctx, client->socket); break; } @@ -588,8 +608,8 @@ int japi_start_server(japi_context *ctx, const char *port) return -1; } if (ctx->max_clients == 0 || ctx->num_clients < ctx->max_clients) { - japi_add_client(ctx,client_socket); - prntdbg("client %d added\n",client_socket); + japi_add_client(ctx, client_socket); + prntdbg("client %d added\n", client_socket); } else { close(client_socket); } @@ -597,7 +617,7 @@ int japi_start_server(japi_context *ctx, const char *port) } /* Clean up */ - japi_remove_all_clients(ctx); + japi_remove_all_clients(ctx); close(server_socket); @@ -633,7 +653,9 @@ void japi_cmd_list(japi_context *ctx, json_object *request, json_object *respons /* * Default handler for reacting to unknown requests. */ -void japi_request_not_found_handler(japi_context *ctx, json_object *request, json_object *response) +void japi_request_not_found_handler(japi_context *ctx, json_object *request, + json_object *response) { - json_object_object_add(response, "error", json_object_new_string("no request handler found")); + json_object_object_add(response, "error", + json_object_new_string("no request handler found")); } \ No newline at end of file diff --git a/src/japi_pushsrv.c b/src/japi_pushsrv.c index eef0b4f..b48877e 100644 --- a/src/japi_pushsrv.c +++ b/src/japi_pushsrv.c @@ -33,10 +33,10 @@ #include #include -#include #include -#include +#include #include +#include #include #include "japi_intern.h" @@ -46,18 +46,17 @@ #include "rw_n.h" - /*! -* \brief Add client to push service -* -* Add client socket to given push service. -* -* \param socket Socket to add -* \param pushsrv_name The name of the push service -* \param psc JAPI push service context -* -* \returns On success, 0 is returned. On error, -1 if memory allocation failed. -*/ + * \brief Add client to push service + * + * Add client socket to given push service. + * + * \param socket Socket to add + * \param pushsrv_name The name of the push service + * \param psc JAPI push service context + * + * \returns On success, 0 is returned. On error, -1 if memory allocation failed. + */ static int japi_pushsrv_add_client(japi_pushsrv_context *psc, int socket) { japi_client *client; @@ -66,7 +65,7 @@ static int japi_pushsrv_add_client(japi_pushsrv_context *psc, int socket) assert(psc != NULL); assert(socket >= 0); - client = (japi_client*)malloc(sizeof(japi_client)); + client = (japi_client *)malloc(sizeof(japi_client)); if (client == NULL) { perror("ERROR: malloc() failed\n"); return -1; @@ -101,7 +100,8 @@ int japi_pushsrv_remove_client(japi_pushsrv_context *psc, int socket) /* If first element */ if ((client->socket == socket) && (prev == NULL)) { psc->clients = client->next; - prntdbg("removing client %d from pushsrv %s\n",client->socket,psc->pushsrv_name); + prntdbg("removing client %d from pushsrv %s\n", client->socket, + psc->pushsrv_name); free(client); ret = 0; break; @@ -109,14 +109,16 @@ int japi_pushsrv_remove_client(japi_pushsrv_context *psc, int socket) /* If last element */ if ((client->socket == socket) && (client->next == NULL)) { prev->next = NULL; - prntdbg("removing client %d from pushsrv %s\n",client->socket,psc->pushsrv_name); + prntdbg("removing client %d from pushsrv %s\n", client->socket, + psc->pushsrv_name); free(client); ret = 0; break; } if (client->socket == socket) { prev->next = client->next; - prntdbg("removing client %d from pushsrv %s\n",client->socket,psc->pushsrv_name); + prntdbg("removing client %d from pushsrv %s\n", client->socket, + psc->pushsrv_name); free(client); ret = 0; break; @@ -140,12 +142,12 @@ void japi_pushsrv_remove_client_from_all_pushsrv(japi_context *ctx, int socket) assert(ctx != NULL); assert(socket >= 0); - prntdbg("removing client %i from all pushsrv\n",socket); + prntdbg("removing client %i from all pushsrv\n", socket); psc = ctx->push_services; while (psc != NULL) { pthread_mutex_lock(&(psc->lock)); - japi_pushsrv_remove_client(psc,socket); + japi_pushsrv_remove_client(psc, socket); pthread_mutex_unlock(&(psc->lock)); psc = psc->next; } @@ -158,7 +160,7 @@ void japi_pushsrv_subscribe(japi_context *ctx, json_object *jreq, json_object *j { japi_pushsrv_context *psc; json_object *jval; - const char* pushsrv_name; + const char *pushsrv_name; int socket, ret; /* Error handling */ @@ -168,37 +170,41 @@ void japi_pushsrv_subscribe(japi_context *ctx, json_object *jreq, json_object *j psc = ctx->push_services; /* Get the push service name */ - if (!json_object_object_get_ex(jreq,"service",&jval) || jval == NULL) { - json_object_object_add(jresp,"success",json_object_new_boolean(false)); - json_object_object_add(jresp,"message",json_object_new_string("Push service not found.")); + if (!json_object_object_get_ex(jreq, "service", &jval) || jval == NULL) { + json_object_object_add(jresp, "success", json_object_new_boolean(false)); + json_object_object_add(jresp, "message", + json_object_new_string("Push service not found.")); return; } pushsrv_name = json_object_get_string(jval); - ret = json_object_object_get_ex(jreq,"socket",&jval); + ret = json_object_object_get_ex(jreq, "socket", &jval); socket = json_object_get_int(jval); if (!ret | socket < 0) { json_object_object_add(jresp, "success", json_object_new_boolean(false)); - json_object_object_add(jresp, "message" ,json_object_new_string("Subscribing push service to non-existing socket")); + json_object_object_add( + jresp, "message", + json_object_new_string("Subscribing push service to non-existing socket")); return; } /* Search for push service in list and save socket, if found */ while (psc != NULL) { - if (strcasecmp(pushsrv_name,psc->pushsrv_name) == 0) { - ret = japi_pushsrv_add_client(psc,socket); + if (strcasecmp(pushsrv_name, psc->pushsrv_name) == 0) { + ret = japi_pushsrv_add_client(psc, socket); break; } psc = psc->next; } - json_object_object_add(jresp,"service",json_object_new_string(pushsrv_name)); + json_object_object_add(jresp, "service", json_object_new_string(pushsrv_name)); /* Create JSON response object */ if (psc == NULL | ret < 0) { - json_object_object_add(jresp,"success",json_object_new_boolean(false)); - json_object_object_add(jresp,"message",json_object_new_string("Push service not found.")); + json_object_object_add(jresp, "success", json_object_new_boolean(false)); + json_object_object_add(jresp, "message", + json_object_new_string("Push service not found.")); } else { - json_object_object_add(jresp,"success",json_object_new_boolean(true)); + json_object_object_add(jresp, "success", json_object_new_boolean(true)); } } @@ -208,8 +214,8 @@ void japi_pushsrv_subscribe(japi_context *ctx, json_object *jreq, json_object *j void japi_pushsrv_unsubscribe(japi_context *ctx, json_object *jreq, json_object *jresp) { japi_pushsrv_context *psc; - json_object* jval; - const char* pushsrv_name; + json_object *jval; + const char *pushsrv_name; int ret, socket; /* Error handling */ @@ -221,26 +227,31 @@ void japi_pushsrv_unsubscribe(japi_context *ctx, json_object *jreq, json_object bool unsubscribed = false; /* Service unsubscribed? */ /* Get the push service name */ - if (!json_object_object_get_ex(jreq,"service",&jval) || jval == NULL) { - json_object_object_add(jresp,"success",json_object_new_boolean(false)); - json_object_object_add(jresp,"message",json_object_new_string("Push service not found.")); + if (!json_object_object_get_ex(jreq, "service", &jval) || jval == NULL) { + json_object_object_add(jresp, "success", json_object_new_boolean(false)); + json_object_object_add(jresp, "message", + json_object_new_string("Push service not found.")); return; } pushsrv_name = json_object_get_string(jval); - ret = json_object_object_get_ex(jreq,"socket",&jval); + ret = json_object_object_get_ex(jreq, "socket", &jval); socket = json_object_get_int(jval); if (!ret | socket < 0) { json_object_object_add(jresp, "success", json_object_new_boolean(false)); - json_object_object_add(jresp, "message" ,json_object_new_string("Unsubscribing push service from non-existing socket")); + json_object_object_add( + jresp, "message", + json_object_new_string( + "Unsubscribing push service from non-existing socket")); return; } - /* Search for push service in list and remove socket, if found & socket is registered */ + /* Search for push service in list and remove socket, if found & socket is + * registered */ while (psc != NULL) { - if (strcasecmp(pushsrv_name,psc->pushsrv_name) == 0) { + if (strcasecmp(pushsrv_name, psc->pushsrv_name) == 0) { registered = true; - if (japi_pushsrv_remove_client(psc,socket) >= 0) { + if (japi_pushsrv_remove_client(psc, socket) >= 0) { unsubscribed = true; break; } @@ -248,17 +259,21 @@ void japi_pushsrv_unsubscribe(japi_context *ctx, json_object *jreq, json_object psc = psc->next; } - json_object_object_add(jresp,"service",json_object_new_string(pushsrv_name)); + json_object_object_add(jresp, "service", json_object_new_string(pushsrv_name)); /* Create JSON response object */ if (registered && unsubscribed) { /* Subscribed */ - json_object_object_add(jresp,"success",json_object_new_boolean(true)); + json_object_object_add(jresp, "success", json_object_new_boolean(true)); } else if (registered && !unsubscribed) { /* Registered, but not subscribed */ - json_object_object_add(jresp,"success",json_object_new_boolean(false)); - json_object_object_add(jresp,"message",json_object_new_string("Can't unsubscribe a service that wasn't subscribed before.")); + json_object_object_add(jresp, "success", json_object_new_boolean(false)); + json_object_object_add( + jresp, "message", + json_object_new_string( + "Can't unsubscribe a service that wasn't subscribed before.")); } else { /* Not registered */ - json_object_object_add(jresp,"success",json_object_new_boolean(false)); - json_object_object_add(jresp,"message",json_object_new_string("Push service not found.")); + json_object_object_add(jresp, "success", json_object_new_boolean(false)); + json_object_object_add(jresp, "message", + json_object_new_string("Push service not found.")); } } @@ -291,22 +306,23 @@ static void free_pushsrv(japi_pushsrv_context *psc) /* * Registers push-service and returns pointer to that service object. */ -japi_pushsrv_context* japi_pushsrv_register(japi_context* ctx, const char* pushsrv_name) +japi_pushsrv_context *japi_pushsrv_register(japi_context *ctx, const char *pushsrv_name) { japi_pushsrv_context *psc; if (ctx == NULL) { - fprintf(stderr,"ERROR: JAPI context is NULL.\n"); + fprintf(stderr, "ERROR: JAPI context is NULL.\n"); return NULL; } - if ((pushsrv_name == NULL) || (strcmp(pushsrv_name,"") == 0)) { - fprintf(stderr,"ERROR: Push service name is NULL or empty.\n"); + if ((pushsrv_name == NULL) || (strcmp(pushsrv_name, "") == 0)) { + fprintf(stderr, "ERROR: Push service name is NULL or empty.\n"); return NULL; } - if (pushsrv_isredundant(ctx,pushsrv_name)) { - fprintf(stderr,"ERROR: A push service called '%s' was already registered.\n",pushsrv_name); + if (pushsrv_isredundant(ctx, pushsrv_name)) { + fprintf(stderr, "ERROR: A push service called '%s' was already registered.\n", + pushsrv_name); return NULL; } @@ -332,8 +348,8 @@ japi_pushsrv_context* japi_pushsrv_register(japi_context* ctx, const char* pushs psc->enabled = false; psc->userptr = ctx->userptr; - if (pthread_mutex_init(&(psc->lock),NULL) != 0) { - fprintf(stderr,"ERROR: mutex initialization has failed\n"); + if (pthread_mutex_init(&(psc->lock), NULL) != 0) { + fprintf(stderr, "ERROR: mutex initialization has failed\n"); return NULL; } @@ -344,8 +360,9 @@ japi_pushsrv_context* japi_pushsrv_register(japi_context* ctx, const char* pushs return psc; } -/* - * Remove push service context from japi context, unsubscribe for all clients and free memory +/* + * Remove push service context from japi context, unsubscribe for all clients and free + * memory */ int japi_pushsrv_destroy(japi_context *ctx, japi_pushsrv_context *psc) { @@ -355,7 +372,7 @@ int japi_pushsrv_destroy(japi_context *ctx, japi_pushsrv_context *psc) assert(ctx != NULL); if (psc == NULL) { - fprintf(stderr,"ERROR: push service context is NULL\n"); + fprintf(stderr, "ERROR: push service context is NULL\n"); return -1; } @@ -378,12 +395,13 @@ int japi_pushsrv_destroy(japi_context *ctx, japi_pushsrv_context *psc) psc_iter = psc_next; } - /* Iterates through push service client list and frees memory for every element and for the push service themself */ + /* Iterates through push service client list and frees memory for every element and + * for the push service themself */ client = psc->clients; pthread_mutex_lock(&(psc->lock)); while (client != NULL) { client_next = client->next; - japi_pushsrv_remove_client(psc,client->socket); + japi_pushsrv_remove_client(psc, client->socket); client = client_next; } pthread_mutex_unlock(&(psc->lock)); @@ -414,7 +432,7 @@ void japi_pushsrv_list(japi_context *ctx, json_object *request, json_object *res /* Iterate through push service list and return JSON object */ while (psc != NULL) { jstring = json_object_new_string(psc->pushsrv_name); /* Create JSON-string */ - json_object_array_add(jarray,jstring); /* Add string to JSON array */ + json_object_array_add(jarray, jstring); /* Add string to JSON array */ psc = psc->next; } @@ -436,7 +454,7 @@ int japi_pushsrv_sendmsg(japi_pushsrv_context *psc, json_object *jmsg_data) /* Return -1 if there is no message to send */ if (jmsg_data == NULL) { - fprintf(stderr,"ERROR: Nothing to send.\n"); + fprintf(stderr, "ERROR: Nothing to send.\n"); return -1; } @@ -450,9 +468,12 @@ int japi_pushsrv_sendmsg(japi_pushsrv_context *psc, json_object *jmsg_data) jmsg = json_object_new_object(); jdata = NULL; - json_object_object_add(jmsg,"japi_pushsrv",json_object_new_string(psc->pushsrv_name)); - jdata = json_object_get(jmsg_data); // increment refcount before calling json_object_object_add as jmesg_data may still be in use by the caller - json_object_object_add(jmsg,"data",jdata); + json_object_object_add(jmsg, "japi_pushsrv", + json_object_new_string(psc->pushsrv_name)); + jdata = json_object_get( + jmsg_data); // increment refcount before calling json_object_object_add as + // jmesg_data may still be in use by the caller + json_object_object_add(jmsg, "data", jdata); msg = japi_get_jobj_as_ndstr(jmsg); json_object_put(jmsg); @@ -461,16 +482,20 @@ int japi_pushsrv_sendmsg(japi_pushsrv_context *psc, json_object *jmsg_data) client = psc->clients; while (client != NULL) { - prntdbg("pushsrv '%s': Sending message to client %d\n. Message: '%s'",psc->pushsrv_name,client->socket,msg); + prntdbg("pushsrv '%s': Sending message to client %d\n. Message: '%s'", + psc->pushsrv_name, client->socket, msg); following_client = client->next; // Save pointer to next element ret = write_n(client->socket, msg, strlen(msg)); if (ret <= 0) { /* If write failed print error and unsubscribe client */ - fprintf(stderr, "ERROR: Failed to send push service message to client %i (write returned %i)\n", client->socket, ret); + fprintf(stderr, + "ERROR: Failed to send push service message to client %i (write " + "returned %i)\n", + client->socket, ret); /* Remove client from respective push service and free */ - japi_pushsrv_remove_client(psc,client->socket); + japi_pushsrv_remove_client(psc, client->socket); } else { success++; } @@ -484,14 +509,15 @@ int japi_pushsrv_sendmsg(japi_pushsrv_context *psc, json_object *jmsg_data) } /* - * Wrapper function that is executed by pthread_create and starts the desired push service routine + * Wrapper function that is executed by pthread_create and starts the desired push + * service routine */ static void *generic_pushsrv_runner(void *arg) { japi_pushsrv_context *psc; japi_pushsrv_routine routine; - psc = (japi_pushsrv_context*)arg; + psc = (japi_pushsrv_context *)arg; assert(psc != NULL); @@ -508,19 +534,21 @@ static void *generic_pushsrv_runner(void *arg) int japi_pushsrv_start(japi_pushsrv_context *psc, japi_pushsrv_routine routine) { if (psc == NULL) { - fprintf(stderr,"ERROR: No push service context passed. Not starting thread.\n"); + fprintf(stderr, + "ERROR: No push service context passed. Not starting thread.\n"); return -1; } if (routine == NULL) { - fprintf(stderr,"ERROR: No routine passed. Not starting thread.\n"); + fprintf(stderr, "ERROR: No routine passed. Not starting thread.\n"); return -2; } psc->enabled = true; psc->routine = routine; - if (pthread_create(&(psc->thread_id),NULL,generic_pushsrv_runner,(void*)psc) != 0) { + if (pthread_create(&(psc->thread_id), NULL, generic_pushsrv_runner, (void *)psc) != + 0) { fprintf(stderr, "ERROR: Error creating push service thread.\n"); psc->enabled = false; return -3; @@ -535,12 +563,12 @@ int japi_pushsrv_start(japi_pushsrv_context *psc, japi_pushsrv_routine routine) int japi_pushsrv_stop(japi_pushsrv_context *psc) { if (psc == NULL) { - fprintf(stderr,"ERROR: No push service context passed. Can't stop thread.\n"); + fprintf(stderr, "ERROR: No push service context passed. Can't stop thread.\n"); return -1; } if (psc->enabled == false) { - fprintf(stderr,"ERROR: Thread not running.\n"); + fprintf(stderr, "ERROR: Thread not running.\n"); return -2; } @@ -548,8 +576,9 @@ int japi_pushsrv_stop(japi_pushsrv_context *psc) psc->enabled = false; /* Wait for thread to end and close it */ - if (pthread_join(psc->thread_id,NULL) != 0) { - fprintf(stderr, "ERROR: Error joining push service routine '%s'\n",psc->pushsrv_name); + if (pthread_join(psc->thread_id, NULL) != 0) { + fprintf(stderr, "ERROR: Error joining push service routine '%s'\n", + psc->pushsrv_name); return -3; } From d3e390e259f81580c5fb439b5599103568564c06 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Fri, 19 Jan 2024 10:57:14 +0100 Subject: [PATCH 25/27] Incorporate review --- include/japi.h | 3 ++- src/japi.c | 2 +- src/japi_pushsrv.c | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/japi.h b/include/japi.h index f5df23a..f30bf8f 100644 --- a/include/japi.h +++ b/include/japi.h @@ -130,7 +130,8 @@ int japi_destroy(japi_context *ctx); * -2 for empty request name, * -3 for empty request handler, * -4 for duplicate naming, - * -5 for failed memory allocation, is returned. + * -5 for failed memory allocation, + * -6 for bad request name (starting with "japi_") is returned. */ int japi_register_request(japi_context *ctx, const char *req_name, japi_req_handler req_handler); diff --git a/src/japi.c b/src/japi.c index fdfa87f..57af2e4 100644 --- a/src/japi.c +++ b/src/japi.c @@ -267,7 +267,7 @@ int japi_register_request(japi_context *ctx, const char *req_name, if (ctx->init && strncmp(req_name, bad_req_name, strlen(bad_req_name)) == 0) { fprintf(stderr, "ERROR: Request name is not allowed.\n"); - return -5; + return -6; } req = (japi_request *)malloc(sizeof(japi_request)); diff --git a/src/japi_pushsrv.c b/src/japi_pushsrv.c index b48877e..bf47e8b 100644 --- a/src/japi_pushsrv.c +++ b/src/japi_pushsrv.c @@ -179,7 +179,7 @@ void japi_pushsrv_subscribe(japi_context *ctx, json_object *jreq, json_object *j pushsrv_name = json_object_get_string(jval); ret = json_object_object_get_ex(jreq, "socket", &jval); socket = json_object_get_int(jval); - if (!ret | socket < 0) { + if (!ret || socket < 0) { json_object_object_add(jresp, "success", json_object_new_boolean(false)); json_object_object_add( jresp, "message", @@ -199,7 +199,7 @@ void japi_pushsrv_subscribe(japi_context *ctx, json_object *jreq, json_object *j json_object_object_add(jresp, "service", json_object_new_string(pushsrv_name)); /* Create JSON response object */ - if (psc == NULL | ret < 0) { + if (psc == NULL || ret < 0) { json_object_object_add(jresp, "success", json_object_new_boolean(false)); json_object_object_add(jresp, "message", json_object_new_string("Push service not found.")); @@ -237,7 +237,7 @@ void japi_pushsrv_unsubscribe(japi_context *ctx, json_object *jreq, json_object ret = json_object_object_get_ex(jreq, "socket", &jval); socket = json_object_get_int(jval); - if (!ret | socket < 0) { + if (!ret || socket < 0) { json_object_object_add(jresp, "success", json_object_new_boolean(false)); json_object_object_add( jresp, "message", From f9587882b9c28566f3f569e2f3e32d051477fff4 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Fri, 19 Jan 2024 11:16:35 +0100 Subject: [PATCH 26/27] run clang on tests for the first time changes would have hidden small fix --- test/japi_test.cc | 268 ++++++++++++++++++++++++---------------------- 1 file changed, 141 insertions(+), 127 deletions(-) diff --git a/test/japi_test.cc b/test/japi_test.cc index 41edb6e..b641a5a 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -23,59 +23,60 @@ Copyright (c) 2023 Fraunhofer IIS #include #include -extern "C"{ +extern "C" { #include "japi.h" #include "japi_intern.h" -#include "japi_pushsrv_intern.h" #include "japi_pushsrv.h" +#include "japi_pushsrv_intern.h" #include "japi_utils.h" #include "rw_n.h" } /* The handler for japi_register_request test */ -static void dummy_request_handler(japi_context *ctx, json_object *request, json_object *response) +static void dummy_request_handler(japi_context *ctx, json_object *request, + json_object *response) { /* Not existent dummy request */ - json_object_object_add(response,"value",json_object_new_string("hello world")); + json_object_object_add(response, "value", json_object_new_string("hello world")); } -TEST(JAPI,Init) +TEST(JAPI, Init) { /* On success, a japi_context object is returned. On error, NULL is returned */ EXPECT_TRUE(japi_init(NULL) != NULL); } -TEST(JAPI,GetValueAsX) +TEST(JAPI, GetValueAsX) { bool bval; - const char* sval; + const char *sval; int ival; long long int lval; double dval; json_object *jresp; jresp = json_object_new_object(); - json_object_object_add(jresp,"string",json_object_new_string("value")); - json_object_object_add(jresp,"bool",json_object_new_boolean(true)); - json_object_object_add(jresp,"int",json_object_new_int(10)); - json_object_object_add(jresp,"int64",json_object_new_int64(9000000000000000000)); - json_object_object_add(jresp,"double",json_object_new_double(10.12345)); + json_object_object_add(jresp, "string", json_object_new_string("value")); + json_object_object_add(jresp, "bool", json_object_new_boolean(true)); + json_object_object_add(jresp, "int", json_object_new_int(10)); + json_object_object_add(jresp, "int64", json_object_new_int64(9000000000000000000)); + json_object_object_add(jresp, "double", json_object_new_double(10.12345)); /* On success, string is returned. On error, <0 is returned */ EXPECT_EQ(japi_get_value_as_str(jresp, "string", &sval), 0); - EXPECT_STREQ(sval,"value"); + EXPECT_STREQ(sval, "value"); /* On success, bool is returned. On error, <0 is returned */ - EXPECT_EQ(japi_get_value_as_bool(jresp, "bool",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "bool", &bval), 0); EXPECT_TRUE(bval); /* On success, int is returned. On error, <0 is returned */ - EXPECT_EQ(japi_get_value_as_int(jresp, "int",&ival),0); - EXPECT_EQ(ival,10); + EXPECT_EQ(japi_get_value_as_int(jresp, "int", &ival), 0); + EXPECT_EQ(ival, 10); /* On success, int64 is returned. On error, <0 is returned */ - EXPECT_EQ(japi_get_value_as_int64(jresp, "int64",&lval),0); - EXPECT_EQ(lval,9000000000000000000); + EXPECT_EQ(japi_get_value_as_int64(jresp, "int64", &lval), 0); + EXPECT_EQ(lval, 9000000000000000000); /* On success, double is returned. On error, <0 is returned */ - EXPECT_EQ(japi_get_value_as_double(jresp, "double",&dval),0); - EXPECT_EQ(dval,10.12345); + EXPECT_EQ(japi_get_value_as_double(jresp, "double", &dval), 0); + EXPECT_EQ(dval, 10.12345); /* Test error return values */ /* Given json-object is NULL, expecting -1 */ @@ -107,18 +108,18 @@ TEST(JAPI,GetValueAsX) json_object_put(jresp); } -TEST(JAPI,ProcessMessage) +TEST(JAPI, ProcessMessage) { japi_context *ctx; - char* response; - const char* request; - const char* sval; + char *response; + const char *request; + const char *sval; json_object *jobj; json_object *jdata; int socket; jobj = json_object_new_object(); - jdata= json_object_new_object(); + jdata = json_object_new_object(); request = "{'japi_request':'dummy_request_handler'}"; response = NULL; ctx = japi_init(NULL); @@ -131,26 +132,28 @@ TEST(JAPI,ProcessMessage) EXPECT_EQ(japi_get_value_as_str(jdata, "error", &sval), 0); EXPECT_STREQ("no request handler found", sval); - japi_register_request(ctx,"dummy_request_handler",&dummy_request_handler); + japi_register_request(ctx, "dummy_request_handler", &dummy_request_handler); /* On success, 0 returned. On error, -1 is returned */ - EXPECT_EQ(japi_process_message(ctx, request, &response, socket),0); + EXPECT_EQ(japi_process_message(ctx, request, &response, socket), 0); jobj = json_tokener_parse(response); - json_object_object_get_ex(jobj,"data",&jdata); - EXPECT_EQ(japi_get_value_as_str(jdata,"value",&sval),0); - EXPECT_STREQ("hello world",sval); + json_object_object_get_ex(jobj, "data", &jdata); + EXPECT_EQ(japi_get_value_as_str(jdata, "value", &sval), 0); + EXPECT_STREQ("hello world", sval); /* Clean up */ japi_destroy(ctx); } -TEST(JAPI,IncludeArgsWithResponse) +TEST(JAPI, IncludeArgsWithResponse) { /* Setup */ japi_context *ctx = japi_init(NULL); - char* response = NULL; - const char* sval; - const char* request = "{'japi_request': 'dummy_request_handler', 'args': {'foo': 'bar'}}"; - const char* request_int_args = "{'japi_request': 'dummy_request_handler', 'args': 42}"; + char *response = NULL; + const char *sval; + const char *request = + "{'japi_request': 'dummy_request_handler', 'args': {'foo': 'bar'}}"; + const char *request_int_args = + "{'japi_request': 'dummy_request_handler', 'args': 42}"; json_object *jobj; json_object *jdata; int socket = 4; @@ -161,43 +164,45 @@ TEST(JAPI,IncludeArgsWithResponse) EXPECT_EQ(japi_include_args_in_response(ctx, true), 0); /* Register dummy request handler */ - japi_register_request(ctx,"dummy_request_handler",&dummy_request_handler); + japi_register_request(ctx, "dummy_request_handler", &dummy_request_handler); /* Response should include request arguments object */ - EXPECT_EQ(japi_process_message(ctx, request, &response, socket),0); + EXPECT_EQ(japi_process_message(ctx, request, &response, socket), 0); jobj = json_tokener_parse(response); EXPECT_TRUE(json_object_object_get_ex(jobj, "args", &jdata)); - EXPECT_EQ(japi_get_value_as_str(jdata,"foo",&sval),0); + EXPECT_EQ(japi_get_value_as_str(jdata, "foo", &sval), 0); EXPECT_STREQ("bar", sval); /* Response should include request argument integer */ - EXPECT_EQ(japi_process_message(ctx, request_int_args, &response, socket),0); + EXPECT_EQ(japi_process_message(ctx, request_int_args, &response, socket), 0); jobj = json_tokener_parse(response); EXPECT_TRUE(json_object_object_get_ex(jobj, "args", &jdata)); EXPECT_EQ(42, json_object_get_int(jdata)); - + /* Teardown */ japi_destroy(ctx); } -TEST(JAPI,Register) +TEST(JAPI, Register) { japi_context *ctx; ctx = japi_init(NULL); /* On success, zero is returned. On error, -1..-4 is returned */ - EXPECT_EQ(japi_register_request(ctx,"req_name",&dummy_request_handler),0); - EXPECT_EQ(japi_register_request(NULL,"req_name",&dummy_request_handler),-1); - EXPECT_EQ(japi_register_request(ctx,NULL,&dummy_request_handler),-2); - EXPECT_EQ(japi_register_request(ctx,"req_name",NULL),-3); - - /* Registering the same request name again or an empty request name, should not be possible */ - EXPECT_EQ(japi_register_request(ctx,"req_name",&dummy_request_handler),-4); - EXPECT_EQ(japi_register_request(ctx,"dummy_request_02",&dummy_request_handler),0); // same handler for another name - EXPECT_EQ(japi_register_request(ctx,"",&dummy_request_handler),-2); + EXPECT_EQ(japi_register_request(ctx, "req_name", &dummy_request_handler), 0); + EXPECT_EQ(japi_register_request(NULL, "req_name", &dummy_request_handler), -1); + EXPECT_EQ(japi_register_request(ctx, NULL, &dummy_request_handler), -2); + EXPECT_EQ(japi_register_request(ctx, "req_name", NULL), -3); + + /* Registering the same request name again or an empty request name, should not be + * possible */ + EXPECT_EQ(japi_register_request(ctx, "req_name", &dummy_request_handler), -4); + EXPECT_EQ(japi_register_request(ctx, "dummy_request_02", &dummy_request_handler), + 0); // same handler for another name + EXPECT_EQ(japi_register_request(ctx, "", &dummy_request_handler), -2); /* Request names starting with japi_ are not allowed */ - EXPECT_EQ(japi_register_request(ctx,"japi_test",&dummy_request_handler),-5); + EXPECT_EQ(japi_register_request(ctx, "japi_test", &dummy_request_handler), -5); japi_destroy(ctx); } @@ -212,9 +217,9 @@ TEST(JAPI, ListCommands) jobj = json_object_new_object(); /* Register some test requests */ - japi_register_request(ctx,"test01",&dummy_request_handler); - japi_register_request(ctx,"test02",&dummy_request_handler); - japi_register_request(ctx,"test03",&dummy_request_handler); + japi_register_request(ctx, "test01", &dummy_request_handler); + japi_register_request(ctx, "test02", &dummy_request_handler); + japi_register_request(ctx, "test03", &dummy_request_handler); /* The function to be tested */ japi_cmd_list(ctx, NULL, jobj); @@ -222,43 +227,46 @@ TEST(JAPI, ListCommands) cmd = ctx->requests; /* Iterate commands array & context and compare strings */ - json_object_object_foreach(jobj, key, val) { + json_object_object_foreach(jobj, key, val) + { json_object_object_get_ex(jobj, key, &val); int arraylen = json_object_array_length(val); int i; - json_object * jvalue; + json_object *jvalue; while (cmd != NULL) { - for (i=0; i< arraylen; i++) { + for (i = 0; i < arraylen; i++) { jvalue = json_object_array_get_idx(val, i); - EXPECT_STREQ(json_object_get_string(jvalue),cmd->name); + EXPECT_STREQ(json_object_get_string(jvalue), cmd->name); cmd = cmd->next; } } } } -TEST(JAPI_Push_Service,Register) +TEST(JAPI_Push_Service, Register) { japi_context *ctx; ctx = japi_init(NULL); - /* On success, a pointer to the japi_push_service context is returned. On error, NULL is returned */ - EXPECT_TRUE(japi_pushsrv_register(ctx,"test_pushsrv") != NULL); - EXPECT_TRUE(japi_pushsrv_register(NULL,"test_pushsrv") == NULL); - EXPECT_TRUE(japi_pushsrv_register(ctx,NULL) == NULL); + /* On success, a pointer to the japi_push_service context is returned. On error, + * NULL is returned */ + EXPECT_TRUE(japi_pushsrv_register(ctx, "test_pushsrv") != NULL); + EXPECT_TRUE(japi_pushsrv_register(NULL, "test_pushsrv") == NULL); + EXPECT_TRUE(japi_pushsrv_register(ctx, NULL) == NULL); - /* Registering the same push service name again or an empty push service name, should not be possible */ - EXPECT_TRUE(japi_pushsrv_register(ctx,"test_pushsrv") == NULL); - EXPECT_TRUE(japi_pushsrv_register(ctx,"") == NULL); + /* Registering the same push service name again or an empty push service name, + * should not be possible */ + EXPECT_TRUE(japi_pushsrv_register(ctx, "test_pushsrv") == NULL); + EXPECT_TRUE(japi_pushsrv_register(ctx, "") == NULL); /* Clean up */ japi_destroy(ctx); } -TEST(JAPI_Push_Service,SubscribeAndUnsubscribe) +TEST(JAPI_Push_Service, SubscribeAndUnsubscribe) { int socket; - char* pushsrv_name; + char *pushsrv_name; bool bval; japi_context *ctx; json_object *jreq; @@ -267,7 +275,7 @@ TEST(JAPI_Push_Service,SubscribeAndUnsubscribe) json_object *bad_req; socket = 4; - pushsrv_name = (char*)"test_pushsrv"; + pushsrv_name = (char *)"test_pushsrv"; jreq = json_object_new_object(); jresp = json_object_new_object(); illegal_req = json_object_new_object(); @@ -275,63 +283,62 @@ TEST(JAPI_Push_Service,SubscribeAndUnsubscribe) ctx = japi_init(NULL); /* Build JSON request */ - json_object_object_add(jreq,"service",json_object_new_string(pushsrv_name)); + json_object_object_add(jreq, "service", json_object_new_string(pushsrv_name)); json_object_object_add(jreq, "socket", json_object_new_int(socket)); /* Sub-/unsubscribe before registering, expecting false */ japi_pushsrv_subscribe(ctx, jreq, jresp); - EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "success", &bval), 0); EXPECT_FALSE(bval); japi_pushsrv_unsubscribe(ctx, jreq, jresp); - EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "success", &bval), 0); EXPECT_FALSE(bval); /* Pass illegal JSON request, expecting false */ - json_object_object_add(illegal_req,"service",NULL); + json_object_object_add(illegal_req, "service", NULL); json_object_object_add(illegal_req, "socket", json_object_new_int(socket)); japi_pushsrv_subscribe(ctx, illegal_req, jresp); - EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "success", &bval), 0); EXPECT_FALSE(bval); japi_pushsrv_unsubscribe(ctx, illegal_req, jresp); - EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "success", &bval), 0); EXPECT_FALSE(bval); /* Pass illegal key */ - json_object_object_add(bad_req,"bad_key",json_object_new_string(pushsrv_name)); + json_object_object_add(bad_req, "bad_key", json_object_new_string(pushsrv_name)); json_object_object_add(bad_req, "socket", json_object_new_int(socket)); japi_pushsrv_subscribe(ctx, bad_req, jresp); - EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "success", &bval), 0); EXPECT_FALSE(bval); japi_pushsrv_unsubscribe(ctx, bad_req, jresp); - EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "success", &bval), 0); EXPECT_FALSE(bval); /* Try to unsubscribe without subscribed before, should fail */ - japi_pushsrv_register(ctx,"test_pushsrv"); + japi_pushsrv_register(ctx, "test_pushsrv"); japi_pushsrv_unsubscribe(ctx, jreq, jresp); - EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "success", &bval), 0); EXPECT_FALSE(bval); /* Expect true */ japi_pushsrv_subscribe(ctx, jreq, jresp); - EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "success", &bval), 0); EXPECT_TRUE(bval); japi_pushsrv_unsubscribe(ctx, jreq, jresp); - EXPECT_EQ(japi_get_value_as_bool(jresp, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jresp, "success", &bval), 0); EXPECT_TRUE(bval); /* Clean up */ json_object_put(jresp); - } -TEST(JAPI_Push_Service,List) +TEST(JAPI_Push_Service, List) { japi_context *ctx; japi_pushsrv_context *psc; @@ -341,9 +348,9 @@ TEST(JAPI_Push_Service,List) jobj = json_object_new_object(); /* Register some test services */ - japi_pushsrv_register(ctx,"test01"); - japi_pushsrv_register(ctx,"test02"); - japi_pushsrv_register(ctx,"test03"); + japi_pushsrv_register(ctx, "test01"); + japi_pushsrv_register(ctx, "test02"); + japi_pushsrv_register(ctx, "test03"); /* The function to be tested */ japi_pushsrv_list(ctx, NULL, jobj); @@ -351,15 +358,16 @@ TEST(JAPI_Push_Service,List) psc = ctx->push_services; /* Iterate push service array & context and compare strings */ - json_object_object_foreach(jobj, key, val) { + json_object_object_foreach(jobj, key, val) + { json_object_object_get_ex(jobj, key, &val); int arraylen = json_object_array_length(val); int i; - json_object * jvalue; + json_object *jvalue; while (psc != NULL) { - for (i=0; i< arraylen; i++) { + for (i = 0; i < arraylen; i++) { jvalue = json_object_array_get_idx(val, i); - EXPECT_STREQ(json_object_get_string(jvalue),psc->pushsrv_name); + EXPECT_STREQ(json_object_get_string(jvalue), psc->pushsrv_name); psc = psc->next; } } @@ -370,7 +378,7 @@ TEST(JAPI_Push_Service,List) json_object_put(jobj); } -TEST(JAPI,AddRemoveClient) +TEST(JAPI, AddRemoveClient) { japi_context *ctx; japi_client *client; @@ -379,14 +387,14 @@ TEST(JAPI,AddRemoveClient) ctx = japi_init(NULL); /* Add some clients */ - EXPECT_EQ(japi_add_client(ctx,4),0); - EXPECT_EQ(japi_add_client(ctx,5),0); - EXPECT_EQ(japi_add_client(ctx,6),0); - EXPECT_EQ(japi_add_client(ctx,7),0); + EXPECT_EQ(japi_add_client(ctx, 4), 0); + EXPECT_EQ(japi_add_client(ctx, 5), 0); + EXPECT_EQ(japi_add_client(ctx, 6), 0); + EXPECT_EQ(japi_add_client(ctx, 7), 0); /* Add the same client again */ - EXPECT_EQ(japi_add_client(ctx,5),0); - EXPECT_EQ(japi_add_client(ctx,5),0); + EXPECT_EQ(japi_add_client(ctx, 5), 0); + EXPECT_EQ(japi_add_client(ctx, 5), 0); counter = 0; client = ctx->clients; @@ -395,11 +403,11 @@ TEST(JAPI,AddRemoveClient) client = client->next; } /* Counter should count 6 added clients */ - EXPECT_EQ(counter,6); + EXPECT_EQ(counter, 6); /* Remove some clients */ - EXPECT_EQ(japi_remove_client(ctx,4),0); - EXPECT_EQ(japi_remove_client(ctx,5),0); + EXPECT_EQ(japi_remove_client(ctx, 4), 0); + EXPECT_EQ(japi_remove_client(ctx, 5), 0); counter = 0; client = ctx->clients; @@ -408,14 +416,14 @@ TEST(JAPI,AddRemoveClient) client = client->next; } /* Counter should count 2 less clients */ - EXPECT_EQ(counter,4); + EXPECT_EQ(counter, 4); /* Remove not existent client */ - EXPECT_EQ(japi_remove_client(ctx,12),-1); - EXPECT_EQ(japi_remove_client(ctx,13),-1); + EXPECT_EQ(japi_remove_client(ctx, 12), -1); + EXPECT_EQ(japi_remove_client(ctx, 13), -1); } -TEST(JAPI_Push_Service,AddRemoveClient) +TEST(JAPI_Push_Service, AddRemoveClient) { japi_context *ctx; japi_pushsrv_context *psc; @@ -432,12 +440,14 @@ TEST(JAPI_Push_Service,AddRemoveClient) ctx = japi_init(NULL); /* Build JSON request */ - json_object_object_add(push_status_jreq,"service",json_object_new_string("pushsrv_status")); - json_object_object_add(push_temperature_jreq,"service",json_object_new_string("pushsrv_temperature")); + json_object_object_add(push_status_jreq, "service", + json_object_new_string("pushsrv_status")); + json_object_object_add(push_temperature_jreq, "service", + json_object_new_string("pushsrv_temperature")); /* Register some push services */ - japi_pushsrv_register(ctx,"pushsrv_status"); - japi_pushsrv_register(ctx,"pushsrv_temperature"); + japi_pushsrv_register(ctx, "pushsrv_status"); + japi_pushsrv_register(ctx, "pushsrv_temperature"); /* Add some clients */ json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(4)); @@ -463,7 +473,7 @@ TEST(JAPI_Push_Service,AddRemoveClient) client = client->next; } /* Counter should count 5 clients for get_temperature */ - EXPECT_EQ(counter,5); + EXPECT_EQ(counter, 5); /* Unsubscribe some clients */ json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(5)); @@ -479,16 +489,16 @@ TEST(JAPI_Push_Service,AddRemoveClient) client = client->next; } /* Two less clients should be counted for get_temperature */ - EXPECT_EQ(counter,3); + EXPECT_EQ(counter, 3); /* Unsubscribe client that is not subscribed */ json_object_object_add(push_temperature_jreq, "socket", json_object_new_int(15)); japi_pushsrv_unsubscribe(ctx, push_temperature_jreq, jobj); - EXPECT_EQ(japi_get_value_as_bool(jobj, "success",&bval),0); + EXPECT_EQ(japi_get_value_as_bool(jobj, "success", &bval), 0); EXPECT_FALSE(bval); } -TEST(JAPI_Push_Service,PushServiceDestroy) +TEST(JAPI_Push_Service, PushServiceDestroy) { japi_context *ctx; japi_pushsrv_context *psc_status, *psc_temperature; @@ -496,18 +506,18 @@ TEST(JAPI_Push_Service,PushServiceDestroy) ctx = japi_init(NULL); /* Register some push services */ - psc_temperature = japi_pushsrv_register(ctx,"pushsrv_status"); - psc_status = japi_pushsrv_register(ctx,"pushsrv_temperature"); + psc_temperature = japi_pushsrv_register(ctx, "pushsrv_status"); + psc_status = japi_pushsrv_register(ctx, "pushsrv_temperature"); /* Destroy push services */ - EXPECT_EQ(japi_pushsrv_destroy(ctx, psc_status),0); - EXPECT_EQ(japi_pushsrv_destroy(ctx, psc_temperature),0); + EXPECT_EQ(japi_pushsrv_destroy(ctx, psc_status), 0); + EXPECT_EQ(japi_pushsrv_destroy(ctx, psc_temperature), 0); /* Pass bad push service context */ - EXPECT_EQ(japi_pushsrv_destroy(ctx, NULL),-1); + EXPECT_EQ(japi_pushsrv_destroy(ctx, NULL), -1); } -TEST(JAPI_Push_Service,PushServiceRemoveEntryFromLInkedList) +TEST(JAPI_Push_Service, PushServiceRemoveEntryFromLInkedList) { japi_context *ctx; japi_pushsrv_context *psc01, *psc02, *psc05; @@ -519,21 +529,25 @@ TEST(JAPI_Push_Service,PushServiceRemoveEntryFromLInkedList) /* Register some test services, creates { "services": [ "test05", "test04", "test03", "test02", "test01" ] } */ - psc01 = japi_pushsrv_register(ctx,"test01"); - psc02 = japi_pushsrv_register(ctx,"test02"); - japi_pushsrv_register(ctx,"test03"); - japi_pushsrv_register(ctx,"test04"); - psc05 = japi_pushsrv_register(ctx,"test05"); + psc01 = japi_pushsrv_register(ctx, "test01"); + psc02 = japi_pushsrv_register(ctx, "test02"); + japi_pushsrv_register(ctx, "test03"); + japi_pushsrv_register(ctx, "test04"); + psc05 = japi_pushsrv_register(ctx, "test05"); japi_pushsrv_destroy(ctx, psc02); japi_pushsrv_list(ctx, NULL, jobj); - EXPECT_STREQ(json_object_to_json_string(jobj), "{ \"services\": [ \"test05\", \"test04\", \"test03\", \"test01\" ] }"); + EXPECT_STREQ( + json_object_to_json_string(jobj), + "{ \"services\": [ \"test05\", \"test04\", \"test03\", \"test01\" ] }"); japi_pushsrv_destroy(ctx, psc05); japi_pushsrv_list(ctx, NULL, jobj); - EXPECT_STREQ(json_object_to_json_string(jobj), "{ \"services\": [ \"test04\", \"test03\", \"test01\" ] }"); + EXPECT_STREQ(json_object_to_json_string(jobj), + "{ \"services\": [ \"test04\", \"test03\", \"test01\" ] }"); japi_pushsrv_destroy(ctx, psc01); japi_pushsrv_list(ctx, NULL, jobj); - EXPECT_STREQ(json_object_to_json_string(jobj), "{ \"services\": [ \"test04\", \"test03\" ] }"); + EXPECT_STREQ(json_object_to_json_string(jobj), + "{ \"services\": [ \"test04\", \"test03\" ] }"); } From 5bf2e4b2766be0737ef6889bf6bd570b5c0d2ed0 Mon Sep 17 00:00:00 2001 From: Katja Vornberger Date: Fri, 19 Jan 2024 11:18:10 +0100 Subject: [PATCH 27/27] Adjust test to changed return value --- test/japi_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/japi_test.cc b/test/japi_test.cc index b641a5a..513d5b4 100644 --- a/test/japi_test.cc +++ b/test/japi_test.cc @@ -202,7 +202,7 @@ TEST(JAPI, Register) EXPECT_EQ(japi_register_request(ctx, "", &dummy_request_handler), -2); /* Request names starting with japi_ are not allowed */ - EXPECT_EQ(japi_register_request(ctx, "japi_test", &dummy_request_handler), -5); + EXPECT_EQ(japi_register_request(ctx, "japi_test", &dummy_request_handler), -6); japi_destroy(ctx); }