mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 09:41:06 +00:00
remove proxy api (moved to logos-module-client)
This commit is contained in:
@@ -51,12 +51,6 @@ set(SDK_SOURCES
|
||||
implementations/mock/mock_transport.h
|
||||
implementations/mock/mock_registry.h
|
||||
implementations/mock/logos_mock.h
|
||||
logos_json_utils.cpp
|
||||
logos_json_utils.h
|
||||
logos_core_client.cpp
|
||||
logos_core_client.h
|
||||
logos_sdk_c.cpp
|
||||
logos_sdk_c.h
|
||||
)
|
||||
|
||||
# Create the SDK library as STATIC instead of SHARED
|
||||
@@ -104,9 +98,6 @@ install(FILES
|
||||
logos_transport_factory.h
|
||||
logos_registry.h
|
||||
logos_registry_factory.h
|
||||
logos_json_utils.h
|
||||
logos_core_client.h
|
||||
logos_sdk_c.h
|
||||
DESTINATION include
|
||||
)
|
||||
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
#include "logos_core_client.h"
|
||||
#include "logos_api.h"
|
||||
#include "logos_api_client.h"
|
||||
#include "logos_object.h"
|
||||
#include "logos_json_utils.h"
|
||||
#include <QDebug>
|
||||
|
||||
LogosCoreClient::LogosCoreClient(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_api(new LogosAPI("core", this))
|
||||
{
|
||||
}
|
||||
|
||||
LogosCoreClient::~LogosCoreClient()
|
||||
{
|
||||
}
|
||||
|
||||
LogosAPIClient* LogosCoreClient::clientFor(const QString& pluginName)
|
||||
{
|
||||
return m_api->getClient(pluginName);
|
||||
}
|
||||
|
||||
void LogosCoreClient::callMethodAsync(const QString& pluginName,
|
||||
const QString& methodName,
|
||||
const QString& paramsJson,
|
||||
AsyncCallback callback)
|
||||
{
|
||||
if (!callback) return;
|
||||
|
||||
bool ok = false;
|
||||
QString errorMessage;
|
||||
QVariantList args = LogosJsonUtils::parseMethodParams(paramsJson, &ok, &errorMessage);
|
||||
|
||||
if (!ok) {
|
||||
callback(false, errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
LogosAPIClient* client = m_api->getClient(pluginName);
|
||||
if (!client) {
|
||||
callback(false, QString("Failed to get client for plugin: %1").arg(pluginName));
|
||||
return;
|
||||
}
|
||||
|
||||
client->invokeRemoteMethodAsync(
|
||||
pluginName, methodName, args,
|
||||
[callback, pluginName, methodName](QVariant result) {
|
||||
if (result.isValid()) {
|
||||
QString resultStr;
|
||||
if (result.canConvert<QString>()) {
|
||||
resultStr = result.toString();
|
||||
} else {
|
||||
resultStr = QString("Result of type: %1").arg(result.typeName());
|
||||
}
|
||||
callback(true, QString("Method call successful. Result: %1").arg(resultStr));
|
||||
} else {
|
||||
callback(false, QStringLiteral("Method call returned invalid result"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void LogosCoreClient::subscribeEvent(const QString& pluginName,
|
||||
const QString& eventName,
|
||||
AsyncCallback callback)
|
||||
{
|
||||
if (!callback) return;
|
||||
|
||||
LogosAPIClient* client = m_api->getClient(pluginName);
|
||||
if (!client) {
|
||||
qWarning() << "LogosCoreClient: Failed to get client for event subscription:" << pluginName;
|
||||
return;
|
||||
}
|
||||
|
||||
LogosObject* obj = client->requestObject(pluginName);
|
||||
if (!obj) {
|
||||
qWarning() << "LogosCoreClient: Failed to get object for event subscription:" << pluginName;
|
||||
return;
|
||||
}
|
||||
|
||||
client->onEvent(obj, eventName,
|
||||
[callback](const QString& evName, const QVariantList& evData) {
|
||||
QString json = LogosJsonUtils::formatEventJson(evName, evData);
|
||||
callback(true, json);
|
||||
});
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
#ifndef LOGOS_CORE_CLIENT_H
|
||||
#define LOGOS_CORE_CLIENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QHash>
|
||||
#include <functional>
|
||||
|
||||
class LogosAPI;
|
||||
class LogosAPIClient;
|
||||
class LogosObject;
|
||||
|
||||
/**
|
||||
* @brief LogosCoreClient provides a high-level async interface for calling
|
||||
* plugin methods and subscribing to events from the "core" (host) perspective.
|
||||
*
|
||||
* Unlike creating ephemeral LogosAPI instances per call, this class maintains
|
||||
* a single persistent LogosAPI("core") and reuses cached client connections.
|
||||
*
|
||||
* It uses LogosAPIClient::invokeRemoteMethodAsync for connection-aware calls,
|
||||
* eliminating manual QTimer-based connection delays.
|
||||
*/
|
||||
class LogosCoreClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using AsyncCallback = std::function<void(bool success, const QString& message)>;
|
||||
|
||||
explicit LogosCoreClient(QObject* parent = nullptr);
|
||||
~LogosCoreClient();
|
||||
|
||||
/**
|
||||
* Call a plugin method asynchronously, with parameters provided as a JSON
|
||||
* string in the [{name,value,type},...] format used by FFI consumers.
|
||||
* The callback receives (success, resultMessage).
|
||||
*/
|
||||
void callMethodAsync(const QString& pluginName,
|
||||
const QString& methodName,
|
||||
const QString& paramsJson,
|
||||
AsyncCallback callback);
|
||||
|
||||
/**
|
||||
* Subscribe to an event from a plugin. The callback fires each time
|
||||
* the event is emitted, with a JSON-formatted message.
|
||||
*/
|
||||
void subscribeEvent(const QString& pluginName,
|
||||
const QString& eventName,
|
||||
AsyncCallback callback);
|
||||
|
||||
/**
|
||||
* Get (or lazily create) a LogosAPIClient for the named plugin.
|
||||
*/
|
||||
LogosAPIClient* clientFor(const QString& pluginName);
|
||||
|
||||
private:
|
||||
LogosAPI* m_api;
|
||||
};
|
||||
|
||||
#endif // LOGOS_CORE_CLIENT_H
|
||||
@@ -1,115 +0,0 @@
|
||||
#include "logos_json_utils.h"
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QDebug>
|
||||
|
||||
namespace LogosJsonUtils {
|
||||
|
||||
QVariant jsonParamToVariant(const QJsonObject& param)
|
||||
{
|
||||
QString name = param.value("name").toString();
|
||||
QString value = param.value("value").toString();
|
||||
QString type = param.value("type").toString();
|
||||
|
||||
qDebug() << "LogosJsonUtils: Converting param:" << name << "value:" << value << "type:" << type;
|
||||
|
||||
if (type == "string" || type == "QString") {
|
||||
return QVariant(value);
|
||||
} else if (type == "int" || type == "integer") {
|
||||
bool ok;
|
||||
int intValue = value.toInt(&ok);
|
||||
return ok ? QVariant(intValue) : QVariant();
|
||||
} else if (type == "bool" || type == "boolean") {
|
||||
if (value.toLower() == "true" || value == "1") {
|
||||
return QVariant(true);
|
||||
} else if (value.toLower() == "false" || value == "0") {
|
||||
return QVariant(false);
|
||||
}
|
||||
return QVariant();
|
||||
} else if (type == "double" || type == "float") {
|
||||
bool ok;
|
||||
double doubleValue = value.toDouble(&ok);
|
||||
return ok ? QVariant(doubleValue) : QVariant();
|
||||
} else {
|
||||
qWarning() << "LogosJsonUtils: Unknown parameter type:" << type << "- treating as string";
|
||||
return QVariant(value);
|
||||
}
|
||||
}
|
||||
|
||||
QVariantList parseMethodParams(const QString& json, bool* ok, QString* errorMessage)
|
||||
{
|
||||
QVariantList args;
|
||||
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(json.toUtf8(), &parseError);
|
||||
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
if (ok) *ok = false;
|
||||
if (errorMessage) *errorMessage = QString("JSON parse error: %1").arg(parseError.errorString());
|
||||
return args;
|
||||
}
|
||||
|
||||
QJsonArray paramsArray = jsonDoc.array();
|
||||
|
||||
for (const QJsonValue& paramValue : paramsArray) {
|
||||
if (paramValue.isObject()) {
|
||||
QJsonObject paramObj = paramValue.toObject();
|
||||
QVariant variant = jsonParamToVariant(paramObj);
|
||||
if (variant.isValid()) {
|
||||
args.append(variant);
|
||||
} else {
|
||||
if (ok) *ok = false;
|
||||
if (errorMessage) *errorMessage = QString("Invalid parameter: %1").arg(paramObj.value("name").toString());
|
||||
return QVariantList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) *ok = true;
|
||||
return args;
|
||||
}
|
||||
|
||||
QString variantToJsonString(const QVariant& value)
|
||||
{
|
||||
if (!value.isValid())
|
||||
return QStringLiteral("null");
|
||||
|
||||
switch (value.userType()) {
|
||||
case QMetaType::Bool:
|
||||
return value.toBool() ? QStringLiteral("true") : QStringLiteral("false");
|
||||
case QMetaType::Int:
|
||||
case QMetaType::LongLong:
|
||||
return QString::number(value.toLongLong());
|
||||
case QMetaType::Double:
|
||||
case QMetaType::Float:
|
||||
return QString::number(value.toDouble());
|
||||
case QMetaType::QString:
|
||||
return value.toString();
|
||||
case QMetaType::QVariantMap: {
|
||||
QJsonDocument doc(QJsonObject::fromVariantMap(value.toMap()));
|
||||
return QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
|
||||
}
|
||||
case QMetaType::QVariantList: {
|
||||
QJsonDocument doc(QJsonArray::fromVariantList(value.toList()));
|
||||
return QString::fromUtf8(doc.toJson(QJsonDocument::Compact));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
QString formatEventJson(const QString& eventName, const QVariantList& data)
|
||||
{
|
||||
QString result = QString("{\"event\":\"%1\",\"data\":[").arg(eventName);
|
||||
for (int i = 0; i < data.size(); ++i) {
|
||||
if (i > 0) result += ",";
|
||||
result += QString("\"%1\"").arg(data[i].toString());
|
||||
}
|
||||
result += "]}";
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace LogosJsonUtils
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef LOGOS_JSON_UTILS_H
|
||||
#define LOGOS_JSON_UTILS_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace LogosJsonUtils {
|
||||
|
||||
/**
|
||||
* Convert a single JSON parameter object {name, value, type} to a QVariant.
|
||||
* Supports types: string, QString, int, integer, bool, boolean, double, float.
|
||||
* Unknown types are treated as strings.
|
||||
* Returns invalid QVariant for unparseable values (e.g. "abc" as int).
|
||||
*/
|
||||
QVariant jsonParamToVariant(const QJsonObject& param);
|
||||
|
||||
/**
|
||||
* Parse a JSON string containing an array of {name, value, type} objects
|
||||
* into a QVariantList. This is the format used by FFI consumers (e.g. logos-js-sdk).
|
||||
*
|
||||
* On success, sets ok=true and returns the converted arguments.
|
||||
* On failure (JSON parse error or invalid parameter), sets ok=false and
|
||||
* fills errorMessage with a description.
|
||||
*/
|
||||
QVariantList parseMethodParams(const QString& json, bool* ok = nullptr, QString* errorMessage = nullptr);
|
||||
|
||||
/**
|
||||
* Convert a QVariant result into a JSON-formatted string suitable for
|
||||
* returning through the C callback API.
|
||||
*/
|
||||
QString variantToJsonString(const QVariant& value);
|
||||
|
||||
/**
|
||||
* Format event data (event name + QVariantList payload) as a JSON string
|
||||
* matching the format expected by FFI consumers:
|
||||
* {"event":"<name>","data":["<val1>","<val2>",...]}
|
||||
*/
|
||||
QString formatEventJson(const QString& eventName, const QVariantList& data);
|
||||
|
||||
}
|
||||
|
||||
#endif // LOGOS_JSON_UTILS_H
|
||||
@@ -1,62 +0,0 @@
|
||||
#include "logos_sdk_c.h"
|
||||
#include "logos_core_client.h"
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
static LogosCoreClient* s_coreClient = nullptr;
|
||||
|
||||
static LogosCoreClient* ensureClient()
|
||||
{
|
||||
if (!s_coreClient)
|
||||
s_coreClient = new LogosCoreClient();
|
||||
return s_coreClient;
|
||||
}
|
||||
|
||||
void logos_sdk_call_method_async(
|
||||
const char* plugin_name,
|
||||
const char* method_name,
|
||||
const char* params_json,
|
||||
LogosSdkCallback callback,
|
||||
void* user_data)
|
||||
{
|
||||
if (!callback) return;
|
||||
|
||||
if (!plugin_name || !method_name) {
|
||||
callback(0, "Plugin name or method name is null", user_data);
|
||||
return;
|
||||
}
|
||||
|
||||
QString pluginStr = QString::fromUtf8(plugin_name);
|
||||
QString methodStr = QString::fromUtf8(method_name);
|
||||
QString paramsStr = params_json ? QString::fromUtf8(params_json) : QStringLiteral("[]");
|
||||
|
||||
ensureClient()->callMethodAsync(pluginStr, methodStr, paramsStr,
|
||||
[callback, user_data](bool success, const QString& message) {
|
||||
QByteArray msgBytes = message.toUtf8();
|
||||
callback(success ? 1 : 0, msgBytes.constData(), user_data);
|
||||
});
|
||||
}
|
||||
|
||||
void logos_sdk_register_event(
|
||||
const char* plugin_name,
|
||||
const char* event_name,
|
||||
LogosSdkCallback callback,
|
||||
void* user_data)
|
||||
{
|
||||
if (!plugin_name || !event_name || !callback) return;
|
||||
|
||||
QString pluginStr = QString::fromUtf8(plugin_name);
|
||||
QString eventStr = QString::fromUtf8(event_name);
|
||||
|
||||
ensureClient()->subscribeEvent(pluginStr, eventStr,
|
||||
[callback, user_data](bool success, const QString& message) {
|
||||
QByteArray msgBytes = message.toUtf8();
|
||||
callback(success ? 1 : 0, msgBytes.constData(), user_data);
|
||||
});
|
||||
}
|
||||
|
||||
void logos_sdk_shutdown()
|
||||
{
|
||||
delete s_coreClient;
|
||||
s_coreClient = nullptr;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
#ifndef LOGOS_SDK_C_H
|
||||
#define LOGOS_SDK_C_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* C callback type matching the AsyncCallback in logos_core.h:
|
||||
* void(int result, const char* message, void* user_data)
|
||||
*/
|
||||
typedef void (*LogosSdkCallback)(int result, const char* message, void* user_data);
|
||||
|
||||
/**
|
||||
* Call a plugin method asynchronously.
|
||||
*
|
||||
* @param plugin_name Target plugin name
|
||||
* @param method_name Method to call
|
||||
* @param params_json JSON array of [{name,value,type},...] parameters (may be NULL for "[]")
|
||||
* @param callback Receives (1=success/0=failure, message, user_data) on completion
|
||||
* @param user_data Opaque pointer passed through to callback
|
||||
*/
|
||||
void logos_sdk_call_method_async(
|
||||
const char* plugin_name,
|
||||
const char* method_name,
|
||||
const char* params_json,
|
||||
LogosSdkCallback callback,
|
||||
void* user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* Register an event listener for a specific event from a plugin.
|
||||
*
|
||||
* @param plugin_name Plugin that emits the event
|
||||
* @param event_name Event name to listen for
|
||||
* @param callback Receives (1, json_event_data, user_data) each time event fires
|
||||
* @param user_data Opaque pointer passed through to callback
|
||||
*/
|
||||
void logos_sdk_register_event(
|
||||
const char* plugin_name,
|
||||
const char* event_name,
|
||||
LogosSdkCallback callback,
|
||||
void* user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* Shut down the SDK's internal core client, releasing connections.
|
||||
* Safe to call multiple times. After this, async calls will lazily re-create
|
||||
* the client on next use.
|
||||
*/
|
||||
void logos_sdk_shutdown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LOGOS_SDK_C_H
|
||||
+1
-4
@@ -36,10 +36,7 @@ pkgs.stdenv.mkDerivation {
|
||||
qt_provider_object.h qt_provider_object.cpp \
|
||||
logos_transport.h logos_transport_factory.h logos_transport_factory.cpp \
|
||||
logos_registry.h logos_registry_factory.h logos_registry_factory.cpp \
|
||||
plugin_registry.h \
|
||||
logos_json_utils.h logos_json_utils.cpp \
|
||||
logos_core_client.h logos_core_client.cpp \
|
||||
logos_sdk_c.h logos_sdk_c.cpp; do
|
||||
plugin_registry.h; do
|
||||
if [ -f cpp/$file ]; then
|
||||
cp cpp/$file $out/include/cpp/
|
||||
fi
|
||||
|
||||
@@ -38,9 +38,6 @@ add_executable(sdk_tests
|
||||
test_local_transport_integration.cpp
|
||||
test_event_system.cpp
|
||||
test_provider_dispatch.cpp
|
||||
test_logos_json_utils.cpp
|
||||
test_logos_core_client.cpp
|
||||
test_logos_sdk_c.cpp
|
||||
fixtures/sample_provider.cpp
|
||||
${GENERATED_DISPATCH}
|
||||
)
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <QCoreApplication>
|
||||
#include "logos_mock.h"
|
||||
#include "logos_core_client.h"
|
||||
|
||||
class LogosCoreClientTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override { m_mock = new LogosMockSetup(); }
|
||||
void TearDown() override { delete m_mock; }
|
||||
LogosMockSetup* m_mock = nullptr;
|
||||
|
||||
void processEvents() {
|
||||
for (int i = 0; i < 10; ++i)
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(LogosCoreClientTest, CallMethodAsync_SuccessfulCall)
|
||||
{
|
||||
m_mock->when("test_module", "greet").thenReturn(QVariant("hello world"));
|
||||
|
||||
LogosCoreClient client;
|
||||
bool called = false;
|
||||
bool success = false;
|
||||
QString message;
|
||||
|
||||
client.callMethodAsync("test_module", "greet", "[]",
|
||||
[&](bool s, const QString& m) { called = true; success = s; message = m; });
|
||||
|
||||
processEvents();
|
||||
|
||||
EXPECT_TRUE(called);
|
||||
EXPECT_TRUE(success);
|
||||
EXPECT_TRUE(message.contains("hello world"));
|
||||
}
|
||||
|
||||
TEST_F(LogosCoreClientTest, CallMethodAsync_WithJsonParams)
|
||||
{
|
||||
m_mock->when("math", "add").thenReturn(QVariant(30));
|
||||
|
||||
LogosCoreClient client;
|
||||
bool called = false;
|
||||
bool success = false;
|
||||
|
||||
QString params = R"([{"name":"a","value":"10","type":"int"},{"name":"b","value":"20","type":"int"}])";
|
||||
client.callMethodAsync("math", "add", params,
|
||||
[&](bool s, const QString&) { called = true; success = s; });
|
||||
|
||||
processEvents();
|
||||
|
||||
EXPECT_TRUE(called);
|
||||
EXPECT_TRUE(success);
|
||||
EXPECT_TRUE(m_mock->wasCalled("math", "add"));
|
||||
}
|
||||
|
||||
TEST_F(LogosCoreClientTest, CallMethodAsync_InvalidJsonReportsError)
|
||||
{
|
||||
LogosCoreClient client;
|
||||
bool called = false;
|
||||
bool success = true;
|
||||
QString message;
|
||||
|
||||
client.callMethodAsync("mod", "fn", "not valid json",
|
||||
[&](bool s, const QString& m) { called = true; success = s; message = m; });
|
||||
|
||||
EXPECT_TRUE(called);
|
||||
EXPECT_FALSE(success);
|
||||
EXPECT_TRUE(message.contains("JSON parse error"));
|
||||
}
|
||||
|
||||
TEST_F(LogosCoreClientTest, CallMethodAsync_InvalidParamReportsError)
|
||||
{
|
||||
LogosCoreClient client;
|
||||
bool called = false;
|
||||
bool success = true;
|
||||
QString message;
|
||||
|
||||
QString params = R"([{"name":"a","value":"abc","type":"int"}])";
|
||||
client.callMethodAsync("mod", "fn", params,
|
||||
[&](bool s, const QString& m) { called = true; success = s; message = m; });
|
||||
|
||||
EXPECT_TRUE(called);
|
||||
EXPECT_FALSE(success);
|
||||
EXPECT_TRUE(message.contains("Invalid parameter"));
|
||||
}
|
||||
|
||||
TEST_F(LogosCoreClientTest, CallMethodAsync_NullCallbackIsNoOp)
|
||||
{
|
||||
LogosCoreClient client;
|
||||
client.callMethodAsync("mod", "fn", "[]", nullptr);
|
||||
}
|
||||
|
||||
TEST_F(LogosCoreClientTest, CallMethodAsync_InvalidResultReportsError)
|
||||
{
|
||||
// No expectation set -> mock returns invalid QVariant
|
||||
m_mock->when("mod", "other").thenReturn(QVariant(1));
|
||||
|
||||
LogosCoreClient client;
|
||||
bool called = false;
|
||||
bool success = true;
|
||||
QString message;
|
||||
|
||||
client.callMethodAsync("mod", "missing_fn", "[]",
|
||||
[&](bool s, const QString& m) { called = true; success = s; message = m; });
|
||||
|
||||
processEvents();
|
||||
|
||||
EXPECT_TRUE(called);
|
||||
EXPECT_FALSE(success);
|
||||
EXPECT_TRUE(message.contains("invalid result"));
|
||||
}
|
||||
|
||||
TEST_F(LogosCoreClientTest, ClientFor_ReturnsSameClientForSamePlugin)
|
||||
{
|
||||
LogosCoreClient client;
|
||||
auto* c1 = client.clientFor("mod");
|
||||
auto* c2 = client.clientFor("mod");
|
||||
EXPECT_EQ(c1, c2);
|
||||
}
|
||||
|
||||
TEST_F(LogosCoreClientTest, ClientFor_ReturnsDifferentForDifferentPlugins)
|
||||
{
|
||||
LogosCoreClient client;
|
||||
auto* c1 = client.clientFor("mod_a");
|
||||
auto* c2 = client.clientFor("mod_b");
|
||||
EXPECT_NE(c1, c2);
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "logos_json_utils.h"
|
||||
#include <QJsonObject>
|
||||
|
||||
// =============================================================================
|
||||
// jsonParamToVariant tests
|
||||
// =============================================================================
|
||||
|
||||
TEST(LogosJsonUtilsTest, ConvertsStringType)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "arg0"; p["value"] = "hello"; p["type"] = "string";
|
||||
QVariant v = LogosJsonUtils::jsonParamToVariant(p);
|
||||
EXPECT_TRUE(v.isValid());
|
||||
EXPECT_EQ(v.toString(), "hello");
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ConvertsQStringType)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "qt"; p["type"] = "QString";
|
||||
EXPECT_EQ(LogosJsonUtils::jsonParamToVariant(p).toString(), "qt");
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ConvertsIntType)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "42"; p["type"] = "int";
|
||||
QVariant v = LogosJsonUtils::jsonParamToVariant(p);
|
||||
EXPECT_TRUE(v.isValid());
|
||||
EXPECT_EQ(v.toInt(), 42);
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ConvertsIntegerType)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "99"; p["type"] = "integer";
|
||||
EXPECT_EQ(LogosJsonUtils::jsonParamToVariant(p).toInt(), 99);
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ConvertsBoolTrue)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "true"; p["type"] = "bool";
|
||||
EXPECT_TRUE(LogosJsonUtils::jsonParamToVariant(p).toBool());
|
||||
|
||||
p["value"] = "1";
|
||||
EXPECT_TRUE(LogosJsonUtils::jsonParamToVariant(p).toBool());
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ConvertsBoolFalse)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "false"; p["type"] = "bool";
|
||||
EXPECT_FALSE(LogosJsonUtils::jsonParamToVariant(p).toBool());
|
||||
|
||||
p["value"] = "0";
|
||||
EXPECT_FALSE(LogosJsonUtils::jsonParamToVariant(p).toBool());
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ConvertsBooleanType)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "true"; p["type"] = "boolean";
|
||||
EXPECT_TRUE(LogosJsonUtils::jsonParamToVariant(p).toBool());
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ConvertsDoubleType)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "3.14"; p["type"] = "double";
|
||||
EXPECT_NEAR(LogosJsonUtils::jsonParamToVariant(p).toDouble(), 3.14, 0.001);
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ConvertsFloatType)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "2.718"; p["type"] = "float";
|
||||
EXPECT_NEAR(LogosJsonUtils::jsonParamToVariant(p).toDouble(), 2.718, 0.001);
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, InvalidIntReturnsInvalid)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "abc"; p["type"] = "int";
|
||||
EXPECT_FALSE(LogosJsonUtils::jsonParamToVariant(p).isValid());
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, InvalidBoolReturnsInvalid)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "maybe"; p["type"] = "bool";
|
||||
EXPECT_FALSE(LogosJsonUtils::jsonParamToVariant(p).isValid());
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, InvalidDoubleReturnsInvalid)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "xyz"; p["type"] = "double";
|
||||
EXPECT_FALSE(LogosJsonUtils::jsonParamToVariant(p).isValid());
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, UnknownTypeTreatedAsString)
|
||||
{
|
||||
QJsonObject p;
|
||||
p["name"] = "a"; p["value"] = "data"; p["type"] = "custom_thing";
|
||||
QVariant v = LogosJsonUtils::jsonParamToVariant(p);
|
||||
EXPECT_TRUE(v.isValid());
|
||||
EXPECT_EQ(v.toString(), "data");
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// parseMethodParams tests
|
||||
// =============================================================================
|
||||
|
||||
TEST(LogosJsonUtilsTest, ParseMixedParams)
|
||||
{
|
||||
QString json = R"([
|
||||
{"name":"arg0","value":"hello","type":"string"},
|
||||
{"name":"arg1","value":"42","type":"int"},
|
||||
{"name":"arg2","value":"true","type":"bool"},
|
||||
{"name":"arg3","value":"3.14","type":"double"}
|
||||
])";
|
||||
bool ok = false;
|
||||
QString err;
|
||||
QVariantList args = LogosJsonUtils::parseMethodParams(json, &ok, &err);
|
||||
EXPECT_TRUE(ok);
|
||||
EXPECT_TRUE(err.isEmpty());
|
||||
ASSERT_EQ(args.size(), 4);
|
||||
EXPECT_EQ(args[0].toString(), "hello");
|
||||
EXPECT_EQ(args[1].toInt(), 42);
|
||||
EXPECT_TRUE(args[2].toBool());
|
||||
EXPECT_NEAR(args[3].toDouble(), 3.14, 0.001);
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ParseEmptyArray)
|
||||
{
|
||||
bool ok = false;
|
||||
QVariantList args = LogosJsonUtils::parseMethodParams("[]", &ok);
|
||||
EXPECT_TRUE(ok);
|
||||
EXPECT_EQ(args.size(), 0);
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ParseInvalidJson)
|
||||
{
|
||||
bool ok = true;
|
||||
QString err;
|
||||
QVariantList args = LogosJsonUtils::parseMethodParams("not json", &ok, &err);
|
||||
EXPECT_FALSE(ok);
|
||||
EXPECT_TRUE(err.contains("JSON parse error"));
|
||||
EXPECT_EQ(args.size(), 0);
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ParseInvalidParamValue)
|
||||
{
|
||||
QString json = R"([{"name":"a","value":"abc","type":"int"}])";
|
||||
bool ok = true;
|
||||
QString err;
|
||||
QVariantList args = LogosJsonUtils::parseMethodParams(json, &ok, &err);
|
||||
EXPECT_FALSE(ok);
|
||||
EXPECT_TRUE(err.contains("Invalid parameter"));
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ParseSingleString)
|
||||
{
|
||||
QString json = R"([{"name":"arg0","value":"test","type":"string"}])";
|
||||
bool ok = false;
|
||||
QVariantList args = LogosJsonUtils::parseMethodParams(json, &ok);
|
||||
EXPECT_TRUE(ok);
|
||||
ASSERT_EQ(args.size(), 1);
|
||||
EXPECT_EQ(args[0].toString(), "test");
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, ParseNullOkPointer)
|
||||
{
|
||||
QVariantList args = LogosJsonUtils::parseMethodParams("[]");
|
||||
EXPECT_EQ(args.size(), 0);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// variantToJsonString tests
|
||||
// =============================================================================
|
||||
|
||||
TEST(LogosJsonUtilsTest, VariantToJson_Invalid)
|
||||
{
|
||||
EXPECT_EQ(LogosJsonUtils::variantToJsonString(QVariant()), "null");
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, VariantToJson_String)
|
||||
{
|
||||
EXPECT_EQ(LogosJsonUtils::variantToJsonString(QVariant("hello")), "hello");
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, VariantToJson_Int)
|
||||
{
|
||||
EXPECT_EQ(LogosJsonUtils::variantToJsonString(QVariant(42)), "42");
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, VariantToJson_Bool)
|
||||
{
|
||||
EXPECT_EQ(LogosJsonUtils::variantToJsonString(QVariant(true)), "true");
|
||||
EXPECT_EQ(LogosJsonUtils::variantToJsonString(QVariant(false)), "false");
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, VariantToJson_Double)
|
||||
{
|
||||
QString result = LogosJsonUtils::variantToJsonString(QVariant(3.14));
|
||||
EXPECT_TRUE(result.startsWith("3.14"));
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, VariantToJson_Map)
|
||||
{
|
||||
QVariantMap map;
|
||||
map["key"] = "value";
|
||||
QString result = LogosJsonUtils::variantToJsonString(QVariant(map));
|
||||
EXPECT_TRUE(result.contains("key"));
|
||||
EXPECT_TRUE(result.contains("value"));
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// formatEventJson tests
|
||||
// =============================================================================
|
||||
|
||||
TEST(LogosJsonUtilsTest, FormatEventJson_Basic)
|
||||
{
|
||||
QVariantList data;
|
||||
data << QVariant("hello") << QVariant(42);
|
||||
QString result = LogosJsonUtils::formatEventJson("myEvent", data);
|
||||
EXPECT_EQ(result, R"({"event":"myEvent","data":["hello","42"]})");
|
||||
}
|
||||
|
||||
TEST(LogosJsonUtilsTest, FormatEventJson_Empty)
|
||||
{
|
||||
QString result = LogosJsonUtils::formatEventJson("evt", {});
|
||||
EXPECT_EQ(result, R"({"event":"evt","data":[]})");
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <QCoreApplication>
|
||||
#include "logos_mock.h"
|
||||
#include "logos_sdk_c.h"
|
||||
#include <cstring>
|
||||
|
||||
static bool s_called = false;
|
||||
static int s_result = -1;
|
||||
static std::string s_message;
|
||||
static void* s_user_data = nullptr;
|
||||
|
||||
static void resetState()
|
||||
{
|
||||
s_called = false;
|
||||
s_result = -1;
|
||||
s_message.clear();
|
||||
s_user_data = nullptr;
|
||||
}
|
||||
|
||||
static void testCCallback(int result, const char* message, void* user_data)
|
||||
{
|
||||
s_called = true;
|
||||
s_result = result;
|
||||
s_message = message ? message : "";
|
||||
s_user_data = user_data;
|
||||
}
|
||||
|
||||
class LogosSdkCTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
m_mock = new LogosMockSetup();
|
||||
resetState();
|
||||
}
|
||||
void TearDown() override
|
||||
{
|
||||
logos_sdk_shutdown();
|
||||
delete m_mock;
|
||||
}
|
||||
LogosMockSetup* m_mock = nullptr;
|
||||
|
||||
void processEvents() {
|
||||
for (int i = 0; i < 10; ++i)
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(LogosSdkCTest, CallMethodAsync_Success)
|
||||
{
|
||||
m_mock->when("mod", "fn").thenReturn(QVariant("ok"));
|
||||
|
||||
logos_sdk_call_method_async("mod", "fn", "[]", testCCallback, nullptr);
|
||||
processEvents();
|
||||
|
||||
EXPECT_TRUE(s_called);
|
||||
EXPECT_EQ(s_result, 1);
|
||||
EXPECT_TRUE(s_message.find("ok") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, CallMethodAsync_WithParams)
|
||||
{
|
||||
m_mock->when("mod", "fn").thenReturn(QVariant(42));
|
||||
|
||||
const char* params = R"([{"name":"a","value":"hello","type":"string"}])";
|
||||
logos_sdk_call_method_async("mod", "fn", params, testCCallback, nullptr);
|
||||
processEvents();
|
||||
|
||||
EXPECT_TRUE(s_called);
|
||||
EXPECT_EQ(s_result, 1);
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, CallMethodAsync_NullPluginName)
|
||||
{
|
||||
logos_sdk_call_method_async(nullptr, "fn", "[]", testCCallback, nullptr);
|
||||
|
||||
EXPECT_TRUE(s_called);
|
||||
EXPECT_EQ(s_result, 0);
|
||||
EXPECT_TRUE(s_message.find("null") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, CallMethodAsync_NullMethodName)
|
||||
{
|
||||
logos_sdk_call_method_async("mod", nullptr, "[]", testCCallback, nullptr);
|
||||
|
||||
EXPECT_TRUE(s_called);
|
||||
EXPECT_EQ(s_result, 0);
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, CallMethodAsync_NullCallback)
|
||||
{
|
||||
logos_sdk_call_method_async("mod", "fn", "[]", nullptr, nullptr);
|
||||
EXPECT_FALSE(s_called);
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, CallMethodAsync_InvalidJson)
|
||||
{
|
||||
logos_sdk_call_method_async("mod", "fn", "bad json", testCCallback, nullptr);
|
||||
|
||||
EXPECT_TRUE(s_called);
|
||||
EXPECT_EQ(s_result, 0);
|
||||
EXPECT_TRUE(s_message.find("JSON parse error") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, CallMethodAsync_NullParamsDefaultsToEmptyArray)
|
||||
{
|
||||
m_mock->when("mod", "fn").thenReturn(QVariant("ok"));
|
||||
|
||||
logos_sdk_call_method_async("mod", "fn", nullptr, testCCallback, nullptr);
|
||||
processEvents();
|
||||
|
||||
EXPECT_TRUE(s_called);
|
||||
EXPECT_EQ(s_result, 1);
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, CallMethodAsync_PassesUserData)
|
||||
{
|
||||
m_mock->when("mod", "fn").thenReturn(QVariant("ok"));
|
||||
|
||||
int data = 999;
|
||||
logos_sdk_call_method_async("mod", "fn", "[]", testCCallback, &data);
|
||||
processEvents();
|
||||
|
||||
EXPECT_TRUE(s_called);
|
||||
EXPECT_EQ(s_user_data, &data);
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, RegisterEvent_NullParams)
|
||||
{
|
||||
logos_sdk_register_event(nullptr, "evt", testCCallback, nullptr);
|
||||
EXPECT_FALSE(s_called);
|
||||
|
||||
logos_sdk_register_event("mod", nullptr, testCCallback, nullptr);
|
||||
EXPECT_FALSE(s_called);
|
||||
|
||||
logos_sdk_register_event("mod", "evt", nullptr, nullptr);
|
||||
EXPECT_FALSE(s_called);
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, Shutdown_SafeToCallMultipleTimes)
|
||||
{
|
||||
logos_sdk_shutdown();
|
||||
logos_sdk_shutdown();
|
||||
}
|
||||
|
||||
TEST_F(LogosSdkCTest, Shutdown_ThenCallRecreatesClient)
|
||||
{
|
||||
m_mock->when("mod", "fn").thenReturn(QVariant("first"));
|
||||
logos_sdk_call_method_async("mod", "fn", "[]", testCCallback, nullptr);
|
||||
processEvents();
|
||||
EXPECT_EQ(s_result, 1);
|
||||
|
||||
logos_sdk_shutdown();
|
||||
resetState();
|
||||
|
||||
m_mock->when("mod", "fn").thenReturn(QVariant("second"));
|
||||
logos_sdk_call_method_async("mod", "fn", "[]", testCCallback, nullptr);
|
||||
processEvents();
|
||||
EXPECT_EQ(s_result, 1);
|
||||
EXPECT_TRUE(s_message.find("second") != std::string::npos);
|
||||
}
|
||||
Reference in New Issue
Block a user