add test-basic-module-cpp (#17)

* add test-basic-module-cpp

* change to new liblogos api
This commit is contained in:
Dario Lipicar
2026-04-24 22:45:29 -03:00
committed by GitHub
parent 6b7d04b5cd
commit e235f51364
9 changed files with 944 additions and 284 deletions
+22
View File
@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.14)
project(TestBasicModuleCppPlugin LANGUAGES CXX)
if(DEFINED ENV{LOGOS_MODULE_BUILDER_ROOT})
include($ENV{LOGOS_MODULE_BUILDER_ROOT}/cmake/LogosModule.cmake)
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/LogosModule.cmake")
include(cmake/LogosModule.cmake)
else()
message(FATAL_ERROR "LogosModule.cmake not found")
endif()
# Universal module: `interface: "universal"` in metadata.json makes the
# builder run `logos-cpp-generator --from-header` in preConfigure. The
# generator produces `generated_code/test_basic_module_cpp_qt_glue.h` +
# `generated_code/test_basic_module_cpp_dispatch.cpp` which LogosModule.cmake
# picks up automatically — no loader / dispatch source needs to live here.
logos_module(
NAME test_basic_module_cpp
SOURCES
src/test_basic_module_cpp_impl.h
src/test_basic_module_cpp_impl.cpp
)
+22
View File
@@ -0,0 +1,22 @@
{
"name": "test_basic_module_cpp",
"version": "1.0.0",
"type": "core",
"category": "testing",
"description": "Pure-C++ mirror of test_basic_module: every method uses std/LogosMap/LogosList/StdLogosResult (no Qt in the impl), so the generated qt_glue layer is exercised end-to-end.",
"main": "test_basic_module_cpp_plugin",
"interface": "universal",
"dependencies": [],
"nix": {
"packages": {
"build": [],
"runtime": ["nlohmann_json"]
},
"external_libraries": [],
"cmake": {
"find_packages": [],
"extra_sources": []
}
}
}
@@ -0,0 +1,209 @@
#include "test_basic_module_cpp_impl.h"
#include <cstddef> // std::size_t (used in splitString below)
#include <utility> // std::move (used in splitString below)
// Keep return shapes closely aligned with test-basic-module's Qt versions
// so the docker smoke / integration tests can exercise the same matrix
// against both modules. Some values intentionally differ where they
// identify the concrete module implementation — notably `returnString()`
// reports `"test_basic_module_cpp"` so the test can distinguish which of
// the two modules answered the call.
// ── void ─────────────────────────────────────────────────────────────────
void TestBasicModuleCppImpl::doNothing() {}
void TestBasicModuleCppImpl::doNothingWithArgs(const std::string&, int64_t) {}
// ── bool ─────────────────────────────────────────────────────────────────
bool TestBasicModuleCppImpl::returnTrue() { return true; }
bool TestBasicModuleCppImpl::returnFalse() { return false; }
bool TestBasicModuleCppImpl::isPositive(int64_t value) { return value > 0; }
// ── int64_t ──────────────────────────────────────────────────────────────
int64_t TestBasicModuleCppImpl::returnInt() { return 42; }
int64_t TestBasicModuleCppImpl::addInts(int64_t a, int64_t b) { return a + b; }
int64_t TestBasicModuleCppImpl::stringLength(const std::string& s) {
return static_cast<int64_t>(s.size());
}
// ── uint64_t ─────────────────────────────────────────────────────────────
uint64_t TestBasicModuleCppImpl::returnUint() { return 99u; }
uint64_t TestBasicModuleCppImpl::echoUint(uint64_t n) { return n; }
// ── double ───────────────────────────────────────────────────────────────
double TestBasicModuleCppImpl::returnDouble() { return 3.5; }
double TestBasicModuleCppImpl::addDoubles(double a, double b) { return a + b; }
// ── std::string ──────────────────────────────────────────────────────────
std::string TestBasicModuleCppImpl::returnString() { return "test_basic_module_cpp"; }
std::string TestBasicModuleCppImpl::echo(const std::string& input) { return input; }
std::string TestBasicModuleCppImpl::concat(const std::string& a, const std::string& b) {
return a + b;
}
// ── StdLogosResult ───────────────────────────────────────────────────────
// Mirrors test_basic_module's `successResult` / `errorResult` / `…WithMap`
// / `…WithList` / `validateInput` — payloads tuned so the pytest assertion
// matrix can compare row-for-row with the Qt-module expectations.
StdLogosResult TestBasicModuleCppImpl::successResult() {
return {true, "operation succeeded", ""};
}
StdLogosResult TestBasicModuleCppImpl::errorResult() {
return {false, {}, "deliberate error for testing"};
}
StdLogosResult TestBasicModuleCppImpl::resultWithMap() {
nlohmann::json m;
m["name"] = "test";
m["count"] = 42;
m["active"] = true;
return {true, m, ""};
}
StdLogosResult TestBasicModuleCppImpl::resultWithList() {
nlohmann::json list = nlohmann::json::array();
list.push_back({{"id", 1}, {"label", "first"}});
list.push_back({{"id", 2}, {"label", "second"}});
return {true, list, ""};
}
StdLogosResult TestBasicModuleCppImpl::validateInput(const std::string& input) {
if (input.empty()) {
return {false, {}, "input cannot be empty"};
}
nlohmann::json data;
data["input"] = input;
data["length"] = static_cast<int64_t>(input.size());
return {true, data, ""};
}
// ── LogosMap ─────────────────────────────────────────────────────────────
LogosMap TestBasicModuleCppImpl::returnMap() {
LogosMap m;
m["key"] = "value";
m["number"] = 7;
return m;
}
LogosMap TestBasicModuleCppImpl::makeMap(const std::string& key,
const std::string& value) {
LogosMap m;
m[key] = value;
return m;
}
// ── LogosList ────────────────────────────────────────────────────────────
LogosList TestBasicModuleCppImpl::returnList() {
LogosList list = nlohmann::json::array();
list.push_back(1);
list.push_back(2);
list.push_back(3);
return list;
}
LogosList TestBasicModuleCppImpl::makeList(const std::string& a,
const std::string& b) {
LogosList list = nlohmann::json::array();
list.push_back(a);
list.push_back(b);
return list;
}
// ── std::vector<std::string> ─────────────────────────────────────────────
std::vector<std::string> TestBasicModuleCppImpl::returnStringList() {
return {"one", "two", "three"};
}
std::vector<std::string> TestBasicModuleCppImpl::splitString(const std::string& input) {
// Split on ','. Simple, matches the Qt module's semantics.
std::vector<std::string> out;
std::string cur;
for (char c : input) {
if (c == ',') { out.push_back(std::move(cur)); cur.clear(); }
else { cur.push_back(c); }
}
out.push_back(std::move(cur));
return out;
}
// ── std::vector<uint8_t> ─────────────────────────────────────────────────
std::vector<uint8_t> TestBasicModuleCppImpl::returnBytes() {
return {0x01, 0x02, 0x03, 0x04, 0x05};
}
int64_t TestBasicModuleCppImpl::byteArraySize(const std::vector<uint8_t>& data) {
return static_cast<int64_t>(data.size());
}
// ── Parameter types ──────────────────────────────────────────────────────
int64_t TestBasicModuleCppImpl::echoInt(int64_t n) { return n; }
bool TestBasicModuleCppImpl::echoBool(bool b) { return b; }
std::string TestBasicModuleCppImpl::joinStrings(const std::vector<std::string>& list) {
std::string out;
for (std::size_t i = 0; i < list.size(); ++i) {
if (i > 0) out += ",";
out += list[i];
}
return out;
}
// ── 0–5 args ─────────────────────────────────────────────────────────────
// The format-string trick (QString("...arg() / arg()")) the Qt module uses
// doesn't translate directly; plain string concatenation produces the same
// output, which keeps the pytest expected-value strings identical.
static std::string toDec(int64_t n) { return std::to_string(n); }
static std::string toBoolStr(bool b) { return b ? "true" : "false"; }
std::string TestBasicModuleCppImpl::noArgs() {
return "noArgs()";
}
std::string TestBasicModuleCppImpl::oneArg(const std::string& a) {
return "oneArg(" + a + ")";
}
std::string TestBasicModuleCppImpl::twoArgs(const std::string& a, int64_t b) {
return "twoArgs(" + a + ", " + toDec(b) + ")";
}
std::string TestBasicModuleCppImpl::threeArgs(const std::string& a, int64_t b, bool c) {
return "threeArgs(" + a + ", " + toDec(b) + ", " + toBoolStr(c) + ")";
}
std::string TestBasicModuleCppImpl::fourArgs(const std::string& a, int64_t b,
bool c, const std::string& d) {
return "fourArgs(" + a + ", " + toDec(b) + ", " + toBoolStr(c) + ", " + d + ")";
}
std::string TestBasicModuleCppImpl::fiveArgs(const std::string& a, int64_t b,
bool c, const std::string& d, int64_t e) {
return "fiveArgs(" + a + ", " + toDec(b) + ", " + toBoolStr(c) + ", " + d + ", " + toDec(e) + ")";
}
// ── Events ───────────────────────────────────────────────────────────────
void TestBasicModuleCppImpl::emitTestEvent(const std::string& data) {
if (emitEvent) emitEvent("testEvent", data);
}
void TestBasicModuleCppImpl::emitMultiArgEvent(const std::string& name, int64_t count) {
// The generator-wired emitEvent takes (name, data). Pack the multi-arg
// payload as a JSON string so the wire shape is lossless (Python test
// just asserts both fragments appear in the stringified event).
if (emitEvent) {
nlohmann::json payload;
payload["name"] = name;
payload["count"] = count;
emitEvent("multiArgEvent", payload.dump());
}
}
@@ -0,0 +1,111 @@
#pragma once
// Pure-C++ mirror of `test_basic_module`. Exercises every parameter/return
// type the code generator needs to translate between C++ std types and Qt
// on the wire:
//
// void, bool, int64_t, uint64_t, double, std::string,
// std::vector<std::string>, std::vector<uint8_t>,
// LogosMap / LogosList (nlohmann::json aliases), StdLogosResult
//
// Absolutely no Qt headers here — `logos-cpp-generator --from-header`
// parses this file as text to derive method signatures, then emits a
// `test_basic_module_cpp_qt_glue.h` + `_dispatch.cpp` that does all the
// QVariant ↔ std conversion. See spec.md in logos-cpp-sdk/cpp-generator/docs/
// for the full conversion table.
//
// Event emission: the generator detects the `std::function emitEvent`
// member by name and wires it to LogosProviderBase::emitEvent in the
// generated glue layer, so the impl can fire events without touching Qt.
#include <cstdint>
#include <functional>
#include <string>
#include <vector>
#include <logos_json.h> // LogosMap, LogosList (nlohmann::json aliases)
#include <logos_result.h> // StdLogosResult
class TestBasicModuleCppImpl {
public:
TestBasicModuleCppImpl() = default;
~TestBasicModuleCppImpl() = default;
// Generated glue wires this in its ctor — call it to emit events. The
// two-string signature (name, json-serialised data) is the one the
// parser recognises; the generator emits a shim that dispatches to
// LogosProviderBase::emitEvent with a QVariantList payload.
std::function<void(const std::string& eventName, const std::string& data)> emitEvent;
// ── Return type: void ────────────────────────────────────────────────
void doNothing();
void doNothingWithArgs(const std::string& a, int64_t b);
// ── Return type: bool ────────────────────────────────────────────────
bool returnTrue();
bool returnFalse();
bool isPositive(int64_t value);
// ── Return type: int64_t ─────────────────────────────────────────────
int64_t returnInt();
int64_t addInts(int64_t a, int64_t b);
int64_t stringLength(const std::string& s);
// ── Return type: uint64_t ────────────────────────────────────────────
uint64_t returnUint();
uint64_t echoUint(uint64_t n);
// ── Return type: double ──────────────────────────────────────────────
double returnDouble();
double addDoubles(double a, double b);
// ── Return type: std::string ─────────────────────────────────────────
std::string returnString();
std::string echo(const std::string& input);
std::string concat(const std::string& a, const std::string& b);
// ── Return type: StdLogosResult ──────────────────────────────────────
// Generator emits a StdLogosResult → Qt LogosResult conversion in glue,
// so over the wire this comes out the same shape as test_basic_module's
// `LogosResult` methods: { success, value, error }.
StdLogosResult successResult();
StdLogosResult errorResult();
StdLogosResult resultWithMap();
StdLogosResult resultWithList();
StdLogosResult validateInput(const std::string& input);
// ── Return type: LogosMap (json object) ─────────────────────────────
// `jsonReturn=true` → glue calls nlohmannToQVariant to unpack into
// QVariantMap, which then serialises as a regular JSON object over RPC.
LogosMap returnMap();
LogosMap makeMap(const std::string& key, const std::string& value);
// ── Return type: LogosList (json array) ─────────────────────────────
LogosList returnList();
LogosList makeList(const std::string& a, const std::string& b);
// ── Return type: std::vector<std::string> ───────────────────────────
std::vector<std::string> returnStringList();
std::vector<std::string> splitString(const std::string& input);
// ── Return type: std::vector<uint8_t> (bytes) ───────────────────────
std::vector<uint8_t> returnBytes();
int64_t byteArraySize(const std::vector<uint8_t>& data);
// ── Parameter types ─────────────────────────────────────────────────
int64_t echoInt(int64_t n);
bool echoBool(bool b);
std::string joinStrings(const std::vector<std::string>& list);
// ── Argument counts 05 (same matrix as the Qt module) ──────────────
std::string noArgs();
std::string oneArg(const std::string& a);
std::string twoArgs(const std::string& a, int64_t b);
std::string threeArgs(const std::string& a, int64_t b, bool c);
std::string fourArgs(const std::string& a, int64_t b, bool c, const std::string& d);
std::string fiveArgs(const std::string& a, int64_t b, bool c, const std::string& d, int64_t e);
// ── Events ──────────────────────────────────────────────────────────
void emitTestEvent(const std::string& data);
void emitMultiArgEvent(const std::string& name, int64_t count);
};