diff mbox series

[2/4] oe-selftest: add a cpp-example recipe

Message ID 20231207205251.4152034-2-adrian.freihofer@siemens.com
State Accepted, archived
Commit 4904e772470b0d6e5d98ef0344b3f2bf54214661
Headers show
Series [1/4] cmake-qemu.bbclass: make it more usable | expand

Commit Message

Adrian Freihofer Dec. 7, 2023, 8:52 p.m. UTC
This simple C++ project supports compilation with CMake and Meson.
(Autotool support could be added later on.)
It's supposed to be used with oe-selftest.

An artificial project has several advantages over compiling a normal
CMake or Meson based project for testing purposes:
- It is much faster because it can be kept minimalistic
- It can cover multiple odd corner cases
- No one will change it in an unpredictable way
- It can support multiple build tools with only one C++ codebase

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 meta-selftest/recipes-test/cpp/.gitignore     |  1 +
 .../recipes-test/cpp/cmake-example.bb         | 25 ++++++++
 .../recipes-test/cpp/cmake-example/run-ptest  | 10 +++
 .../recipes-test/cpp/cpp-example.inc          | 24 ++++++++
 .../recipes-test/cpp/files/CMakeLists.txt     | 61 +++++++++++++++++++
 .../cpp/files/cpp-example-lib.cpp             | 33 ++++++++++
 .../cpp/files/cpp-example-lib.hpp             | 21 +++++++
 .../recipes-test/cpp/files/cpp-example.cpp    | 18 ++++++
 .../recipes-test/cpp/files/meson.build        | 38 ++++++++++++
 .../recipes-test/cpp/files/meson.options      |  3 +
 .../cpp/files/test-cpp-example.cpp            | 25 ++++++++
 .../recipes-test/cpp/meson-example.bb         | 27 ++++++++
 .../recipes-test/cpp/meson-example/run-ptest  | 10 +++
 13 files changed, 296 insertions(+)
 create mode 100644 meta-selftest/recipes-test/cpp/.gitignore
 create mode 100644 meta-selftest/recipes-test/cpp/cmake-example.bb
 create mode 100644 meta-selftest/recipes-test/cpp/cmake-example/run-ptest
 create mode 100644 meta-selftest/recipes-test/cpp/cpp-example.inc
 create mode 100644 meta-selftest/recipes-test/cpp/files/CMakeLists.txt
 create mode 100644 meta-selftest/recipes-test/cpp/files/cpp-example-lib.cpp
 create mode 100644 meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp
 create mode 100644 meta-selftest/recipes-test/cpp/files/cpp-example.cpp
 create mode 100644 meta-selftest/recipes-test/cpp/files/meson.build
 create mode 100644 meta-selftest/recipes-test/cpp/files/meson.options
 create mode 100644 meta-selftest/recipes-test/cpp/files/test-cpp-example.cpp
 create mode 100644 meta-selftest/recipes-test/cpp/meson-example.bb
 create mode 100644 meta-selftest/recipes-test/cpp/meson-example/run-ptest

Comments

Ross Burton Dec. 13, 2023, 11:10 a.m. UTC | #1
> On 7 Dec 2023, at 20:52, Adrian Freihofer via lists.openembedded.org <adrian.freihofer=gmail.com@lists.openembedded.org> wrote:
> 
> +do_run_tests () {
> +    meson test -C "${B}" --no-rebuild
> +}

Somewhere in <gestures at 100+ feature branches on poky-contrib> is a partial implementation of a generic “run the test suite at build time in qemu-user” which failed miserably because qemu-user is pretty limited.  I suspect it was _too_ generic, by trying to run the tests via ‘qemu-user make check’.

Have you had success with non-trivial test suites managed by cmake/meson where the tooling invokes qemu directly? Generalising this would be very interesting.

Ross
Adrian Freihofer Dec. 13, 2023, 8:15 p.m. UTC | #2
On Wed, 2023-12-13 at 11:10 +0000, Ross Burton wrote:
> 
> 
> > On 7 Dec 2023, at 20:52, Adrian Freihofer via
> > lists.openembedded.org
> > <adrian.freihofer=gmail.com@lists.openembedded.org> wrote:
> > 
> > +do_run_tests () {
> > +    meson test -C "${B}" --no-rebuild
> > +}
> 
> Somewhere in <gestures at 100+ feature branches on poky-contrib> is a
> partial implementation of a generic “run the test suite at build time
> in qemu-user” which failed miserably because qemu-user is pretty
> limited.  I suspect it was _too_ generic, by trying to run the tests
> via ‘qemu-user make check’.

Maybe you mean this one which is older:
https://lists.openembedded.org/g/openembedded-core/topic/89289917

And here is a more recent discussion related to my patches:
https://lists.openembedded.org/g/openembedded-core/topic/102708283#190925

> 
> Have you had success with non-trivial test suites managed by
> cmake/meson where the tooling invokes qemu directly? Generalising
> this would be very interesting.
> 
I probably haven't run some non-trivial test suites yet. But the simple
examples I tried worked very well. Running the tests is also
transparently supported by IDEs with support for cmake and meson
(because cmake and meson integrate qemu). Debugging doesn't work yet,
but as far as I know qemu-usermode would also provide a gdbstub, which
would at least theoretically allow seamless debugger integration.
Especially for the SDK use case with IDE integration I see some
potential in this approach. It allows to develop some unit tests on the
host and finally test and deploy the application and the test binaries
on the remote target device. At least in theory, this could hide the
additional complexity of Cross as much as possible.

However, since there are some corner cases where it does not work, it
is fully optionally integrated. cmake recipes that are known to benefit
from qemu-usermode and work reliably with it can inherit cmake-qemu
instead of cmake. Also the new oe-selftests can be seen as tests for
qemu-usermode, which is a dependency of the kernel anyway and should
therefore be tested somehow.

My intention is to introduce this feature in a very limited scope, at
least in this first step.

Adrian



> Ross
diff mbox series

Patch

diff --git a/meta-selftest/recipes-test/cpp/.gitignore b/meta-selftest/recipes-test/cpp/.gitignore
new file mode 100644
index 00000000000..30d388a12b7
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/.gitignore
@@ -0,0 +1 @@ 
+build*
\ No newline at end of file
diff --git a/meta-selftest/recipes-test/cpp/cmake-example.bb b/meta-selftest/recipes-test/cpp/cmake-example.bb
new file mode 100644
index 00000000000..aecfcf780a3
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/cmake-example.bb
@@ -0,0 +1,25 @@ 
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+SUMMARY = "A C++ example compiled with cmake."
+
+require cpp-example.inc
+
+SRC_URI += "file://CMakeLists.txt"
+
+inherit cmake-qemu
+
+PACKAGECONFIG[failing_test] = "-DFAILING_TEST=ON"
+
+FILES:${PN}-ptest += "${bindir}/test-cmake-example"
+
+do_run_tests () {
+    bbnote ${DESTDIR:+DESTDIR=${DESTDIR} }${CMAKE_VERBOSE} cmake --build '${B}' --target test -- ${EXTRA_OECMAKE_BUILD}
+    eval ${DESTDIR:+DESTDIR=${DESTDIR} }${CMAKE_VERBOSE} cmake --build '${B}' --target test -- ${EXTRA_OECMAKE_BUILD}
+}
+do_run_tests[doc] = "Run cmake --target=test using qemu-user"
+
+addtask do_run_tests after do_compile
diff --git a/meta-selftest/recipes-test/cpp/cmake-example/run-ptest b/meta-selftest/recipes-test/cpp/cmake-example/run-ptest
new file mode 100644
index 00000000000..94b620a1984
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/cmake-example/run-ptest
@@ -0,0 +1,10 @@ 
+#!/bin/sh
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+test-cmake-example
+
+# Note: run-ptests exits with exit value from test-cmake-example
diff --git a/meta-selftest/recipes-test/cpp/cpp-example.inc b/meta-selftest/recipes-test/cpp/cpp-example.inc
new file mode 100644
index 00000000000..ad374be9d08
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/cpp-example.inc
@@ -0,0 +1,24 @@ 
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
+
+DEPENDS += "json-c"
+
+PV = "1.0"
+
+SRC_URI = "\
+    file://cpp-example.cpp \
+    file://cpp-example-lib.hpp \
+    file://cpp-example-lib.cpp \
+    file://test-cpp-example.cpp \
+    file://run-ptest \
+"
+
+S = "${WORKDIR}"
+
+inherit ptest
diff --git a/meta-selftest/recipes-test/cpp/files/CMakeLists.txt b/meta-selftest/recipes-test/cpp/files/CMakeLists.txt
new file mode 100644
index 00000000000..6fa6917d89b
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/files/CMakeLists.txt
@@ -0,0 +1,61 @@ 
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+cmake_minimum_required(VERSION 3.22)
+
+project(cmake-example
+  VERSION 1.0.0
+  LANGUAGES CXX
+)
+
+option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
+option(FAILING_TEST "Compile a failing unit test to test the test infrastructure" OFF)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED On)
+set(CMAKE_CXX_EXTENSIONS Off)
+
+include(GNUInstallDirs)
+
+# Linking a small library makes the example more useful for testing.
+find_package(json-c)
+
+# A simple library linking json-c library found by pkgconfig
+add_library(cmake-example-lib cpp-example-lib.cpp cpp-example-lib.hpp)
+set_target_properties(cmake-example-lib PROPERTIES 
+    VERSION ${PROJECT_VERSION}
+    SOVERSION ${PROJECT_VERSION_MAJOR}
+)
+target_link_libraries(cmake-example-lib PRIVATE json-c::json-c)
+
+install(TARGETS cmake-example-lib
+    INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+# A simple executable linking the library
+add_executable(cmake-example cpp-example.cpp)
+target_link_libraries(cmake-example PRIVATE cmake-example-lib)
+
+install(TARGETS cmake-example
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
+
+# A simple test executable for testing the library
+add_executable(test-cmake-example test-cpp-example.cpp)
+target_link_libraries(test-cmake-example PRIVATE cmake-example-lib)
+
+if (FAILING_TEST)
+    target_compile_definitions(test-cmake-example PRIVATE FAIL_COMPARISON_STR="foo")
+endif(FAILING_TEST)
+
+install(TARGETS test-cmake-example
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+)
+
+include(CTest)
+add_test(NAME test-cmake-example COMMAND test-cmake-example)
diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example-lib.cpp b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.cpp
new file mode 100644
index 00000000000..d3dc976864b
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.cpp
@@ -0,0 +1,33 @@ 
+/*
+ * Copyright OpenEmbedded Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <iostream>
+#include <string>
+#include <json-c/json.h>
+#include "cpp-example-lib.hpp"
+
+const std::string &CppExample::get_string()
+{
+    return test_string;
+}
+
+const char *CppExample::get_json_c_version()
+{
+    return json_c_version();
+}
+
+void CppExample::print_json()
+{
+    struct json_object *jobj;
+    const int flag = JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY;
+
+    jobj = json_object_new_object();
+    json_object_object_add(jobj, "test_string", json_object_new_string(test_string.c_str()));
+
+    std::cout << json_object_to_json_string_ext(jobj, flag) << std::endl;
+
+    json_object_put(jobj); // Delete the json object
+}
diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp
new file mode 100644
index 00000000000..0ad9e7b7b2d
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp
@@ -0,0 +1,21 @@ 
+/*
+ * Copyright OpenEmbedded Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include <string>
+
+struct CppExample
+{
+    inline static const std::string test_string = "cpp-example-lib Magic: 123456789";
+
+    /* Retrieve a constant string */
+    const std::string &get_string();
+    /* Retrieve a constant string from a library */
+    const char *get_json_c_version();
+    /* Call a more advanced function from a library */
+    void print_json();
+};
diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example.cpp b/meta-selftest/recipes-test/cpp/files/cpp-example.cpp
new file mode 100644
index 00000000000..9889554e0cb
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/files/cpp-example.cpp
@@ -0,0 +1,18 @@ 
+/*
+ * Copyright OpenEmbedded Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "cpp-example-lib.hpp"
+
+#include <iostream>
+
+int main()
+{
+    auto cpp_example = CppExample();
+    std::cout << "C++ example linking " << cpp_example.get_string() << std::endl;
+    std::cout << "Linking json-c version " << cpp_example.get_json_c_version() << std::endl;
+    cpp_example.print_json();
+    return 0;
+}
diff --git a/meta-selftest/recipes-test/cpp/files/meson.build b/meta-selftest/recipes-test/cpp/files/meson.build
new file mode 100644
index 00000000000..0e2b55f3a2b
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/files/meson.build
@@ -0,0 +1,38 @@ 
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+project('meson-example', 'cpp',
+    version: '1.0.0',
+    default_options: ['cpp_std=c++17']
+    )
+
+jsoncdep = dependency('json-c')
+
+if get_option('FAILING_TEST').enabled()
+    add_project_arguments('-DFAIL_COMPARISON_STR=foo', language: 'cpp')
+endif
+
+mesonexlib = shared_library('mesonexlib',
+    'cpp-example-lib.cpp', 'cpp-example-lib.hpp',
+	version: meson.project_version(),
+	soversion: meson.project_version().split('.')[0],
+    dependencies : jsoncdep,
+    install : true
+    )
+
+executable('mesonex',
+    'cpp-example.cpp',
+    link_with : mesonexlib,
+    install : true
+    )
+
+test_mesonex = executable('test-mesonex',
+    'test-cpp-example.cpp',
+    link_with : mesonexlib,
+    install : true
+)
+
+test('meson example test', test_mesonex)
diff --git a/meta-selftest/recipes-test/cpp/files/meson.options b/meta-selftest/recipes-test/cpp/files/meson.options
new file mode 100644
index 00000000000..58a0bf9e611
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/files/meson.options
@@ -0,0 +1,3 @@ 
+
+option('FAILING_TEST', type : 'feature', value : 'disabled',
+    description : 'Compile a failing unit test to test the test infrastructure')
diff --git a/meta-selftest/recipes-test/cpp/files/test-cpp-example.cpp b/meta-selftest/recipes-test/cpp/files/test-cpp-example.cpp
new file mode 100644
index 00000000000..83c9bfa8444
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/files/test-cpp-example.cpp
@@ -0,0 +1,25 @@ 
+/*
+* Copyright OpenEmbedded Contributors
+*
+* SPDX-License-Identifier: MIT
+*/
+
+#include "cpp-example-lib.hpp"
+
+#include <iostream>
+
+/* This is for creating a failing test for testing the test infrastructure */
+#ifndef FAIL_COMPARISON_STR
+#define FAIL_COMPARISON_STR ""
+#endif
+
+int main() {
+    auto cpp_example = CppExample();
+    auto ret_string = cpp_example.get_string();
+    if(0 == ret_string.compare(CppExample::test_string + FAIL_COMPARISON_STR)) {
+        std::cout << "PASS: " << ret_string << " = " << CppExample::test_string << std::endl;
+    } else {
+        std::cout << "FAIL: " << ret_string << " != " << CppExample::test_string << std::endl;
+        return 1;
+    }
+}
diff --git a/meta-selftest/recipes-test/cpp/meson-example.bb b/meta-selftest/recipes-test/cpp/meson-example.bb
new file mode 100644
index 00000000000..14a7ca8dc91
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/meson-example.bb
@@ -0,0 +1,27 @@ 
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+SUMMARY = "A C++ example compiled with meson."
+
+require cpp-example.inc
+
+SRC_URI += "\
+    file://meson.build \
+    file://meson.options \
+"
+
+inherit pkgconfig meson
+
+PACKAGECONFIG[failing_test] = "-DFAILING_TEST=enabled"
+
+FILES:${PN}-ptest += "${bindir}/test-mesonex"
+
+do_run_tests () {
+    meson test -C "${B}" --no-rebuild
+}
+do_run_tests[doc] = "Run meson test using qemu-user"
+
+addtask do_run_tests after do_compile
diff --git a/meta-selftest/recipes-test/cpp/meson-example/run-ptest b/meta-selftest/recipes-test/cpp/meson-example/run-ptest
new file mode 100644
index 00000000000..b1804f00961
--- /dev/null
+++ b/meta-selftest/recipes-test/cpp/meson-example/run-ptest
@@ -0,0 +1,10 @@ 
+#!/bin/sh
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+test-mesonex
+
+# Note: run-ptests exits with exit value from test-mesonex