blob: b302deec900c412a77c55c6c3f4c47ce06804c4c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "gtest/gtest.h"
extern "C" {
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <assert.h>
#include <float.h>
#include <ffi.h>
#include "dyn_common.h"
#include "dyn_type.h"
#include "dyn_interface.h"
#include "json_serializer.h"
#include "json_rpc.h"
static void stdLog(void*, int level, const char *file, int line, const char *msg, ...) {
va_list ap;
const char *levels[5] = {"NIL", "ERROR", "WARNING", "INFO", "DEBUG"};
fprintf(stderr, "%s: FILE:%s, LINE:%i, MSG:",levels[level], file, line);
va_start(ap, msg);
vfprintf(stderr, msg, ap);
fprintf(stderr, "\n");
va_end(ap);
}
}
extern "C" {
const char* sourceAvprFile = "{ \
\"protocol\" : \"calculator\", \
\"namespace\" : \"test.rpc\", \
\"version\" : \"1.0.0\", \
\"types\" : [ { \
\"type\" : \"fixed\", \
\"name\" : \"Double\", \
\"alias\": \"double\", \
\"size\" : 8 \
}, { \
\"type\" : \"fixed\", \
\"name\" : \"String\", \
\"alias\": \"string\", \
\"size\" : 8 \
}, { \
\"type\" : \"record\", \
\"name\" : \"TwoDoubles\", \
\"fields\" : [ { \
\"name\" : \"a\", \
\"type\" : \"Double\" \
} , { \
\"name\" : \"b\", \
\"type\" : \"Double\" \
} ] \
}, { \
\"type\" : \"record\", \
\"name\" : \"StatsResult\", \
\"fields\" : [ { \
\"name\" : \"average\", \
\"type\" : \"Double\" \
} , { \
\"name\" : \"min\", \
\"type\" : \"Double\" \
} , { \
\"name\" : \"max\", \
\"type\" : \"Double\" \
} , { \
\"name\" : \"input\", \
\"type\" : { \
\"type\" : \"array\",\
\"items\" : \"Double\"\
} \
} ] \
} ] ,\
\"messages\" : { \
\"add\" : {\
\"index\" : 0, \
\"request\" : [ {\
\"name\" : \"arg1\", \
\"type\" : \"Double\" \
} , { \
\"name\" : \"arg2\", \
\"type\" : \"Double\" \
} ],\
\"response\" : \"Double\" \
}, \
\"sub\" : {\
\"index\" : 1, \
\"request\" : [ {\
\"name\" : \"arg1\", \
\"type\" : \"Double\" \
} , { \
\"name\" : \"arg2\", \
\"type\" : \"Double\" \
} ],\
\"response\" : \"Double\" \
}, \
\"sqrt\" : {\
\"index\" : 2, \
\"request\" : [ {\
\"name\" : \"arg1\", \
\"type\" : \"Double\" \
} ],\
\"response\" : \"Double\" \
}, \
\"stats\" : {\
\"index\" : 3, \
\"request\" : [ {\
\"name\" : \"arg1\", \
\"type\" : { \
\"type\" : \"array\",\
\"items\" : \"Double\"\
} \
} ],\
\"response\" : \"StatsResult\" \
}, \
\"retArray\" : {\
\"index\" : 0, \
\"request\" : [ ],\
\"response\" : { \
\"type\" : \"array\",\
\"items\" : \"TwoDoubles\"\
} \
}, \
\"getName\" : {\
\"index\" : 0, \
\"request\" : [ ],\
\"response\" : \"String\" \
} \
} \
}";
}
class JsonAvprRpcTests : public ::testing::Test {
public:
JsonAvprRpcTests() {
int lvl = 1;
dynCommon_logSetup(stdLog, nullptr, lvl);
dynType_logSetup(stdLog, nullptr,lvl);
dynFunction_logSetup(stdLog, nullptr,lvl);
dynInterface_logSetup(stdLog, nullptr,lvl);
jsonSerializer_logSetup(stdLog, nullptr, lvl);
jsonRpc_logSetup(stdLog, nullptr, lvl);
}
~JsonAvprRpcTests() override {
}
};
TEST_F(JsonAvprRpcTests, prep) {
dyn_function_type * func = dynFunction_parseAvprWithStr(sourceAvprFile, "test.rpc.add");
ASSERT_TRUE(func != nullptr);
int nof_args = dynFunction_nrOfArguments(func);
ASSERT_EQ(4, nof_args);
// Msg
char *result = nullptr;
void *handle = nullptr;
double arg1 = 1.0;
double arg2 = 2.0;
void *args[4];
args[0] = &handle;
args[1] = &arg1;
args[2] = &arg2;
int rc = jsonRpc_prepareInvokeRequest(func, "add", args, &result);
ASSERT_EQ(0, rc);
ASSERT_TRUE(strstr(result, "\"add\"") != nullptr);
ASSERT_TRUE(strstr(result, "1.0") != nullptr);
ASSERT_TRUE(strstr(result, "2.0") != nullptr);
// Reply
const char *reply = "{\"r\": 2.2}";
double calc_result = -1.0;
double *out = &calc_result;
//double **out_ptr = &out;
args[3] = &out;
rc = jsonRpc_handleReply(func, reply, args);
ASSERT_EQ(0, rc);
ASSERT_NEAR(2.2, calc_result, 0.001);
free(result);
dynFunction_destroy(func);
}
// Test handle out
extern "C" {
struct tst_seq {
uint32_t cap;
uint32_t len;
double *buf;
};
struct tst_StatsResult {
double average;
double min;
double max;
tst_seq input;
};
}
TEST_F(JsonAvprRpcTests, handle_out) {
dyn_interface_type * intf = dynInterface_parseAvprWithStr(sourceAvprFile);
ASSERT_TRUE(intf != nullptr);
methods_head *head;
dynInterface_methods(intf, &head);
dyn_function_type *func = nullptr;
method_entry *entry = nullptr;
TAILQ_FOREACH(entry, head, entries) {
if (strcmp(entry->name, "stats") == 0) {
func = entry->dynFunc;
break;
}
}
ASSERT_TRUE(func != nullptr);
int nof_args = dynFunction_nrOfArguments(func);
ASSERT_EQ(3, nof_args);
const char *reply = R"({"r":{"input":[1.0,2.0],"max":2.0,"average":1.5,"min":1.0}})";
void *args[3];
args[0] = nullptr;
args[1] = nullptr;
args[2] = nullptr;
struct tst_StatsResult *result = nullptr;
void *out = &result;
args[2] = &out;
int rc = jsonRpc_handleReply(func, reply, args);
ASSERT_EQ(0, rc);
ASSERT_TRUE(result != nullptr);
ASSERT_EQ(1.5, result->average);
free(result->input.buf);
free(result);
dynInterface_destroy(intf);
}
// Test pre allocated
extern "C" {
struct tst_serv {
void *handle;
int (*add)(void *, double, double, double *);
int (*sub)(void *, double, double, double *);
int (*sqrt)(void *, double, double *);
int (*stats)(void *, tst_seq, tst_StatsResult **);
};
int xadd(void*, double a, double b, double *result) {
*result = a + b;
return 0;
}
}
TEST_F(JsonAvprRpcTests, preallocated) {
dyn_interface_type * intf = dynInterface_parseAvprWithStr(sourceAvprFile);
ASSERT_TRUE(intf != nullptr);
char *result = nullptr;
tst_serv serv {nullptr, xadd, nullptr, nullptr, nullptr};
int rc = jsonRpc_call(intf, &serv, R"({"m": "add", "a": [1.0,2.0]})", &result);
ASSERT_EQ(0, rc);
ASSERT_TRUE(result != nullptr);
ASSERT_TRUE(strstr(result, "3.0") != nullptr);
free(result);
dynInterface_destroy(intf);
}
// Test output
extern "C" {
int xstats(void*, struct tst_seq input, struct tst_StatsResult **out) {
assert(out != nullptr);
assert(*out == nullptr);
double total = 0.0;
unsigned int count = 0;
auto max = DBL_MIN;
auto min = DBL_MAX;
unsigned int i;
for (i = 0; i<input.len; i += 1) {
total += input.buf[i];
count += 1;
if (input.buf[i] > max) {
max = input.buf[i];
}
if (input.buf[i] < min) {
min = input.buf[i];
}
}
auto result = static_cast<tst_StatsResult *>(calloc(1, sizeof(tst_StatsResult)));
EXPECT_TRUE(result != nullptr);
if (count>0) {
result->average = total / count;
}
result->min = min;
result->max = max;
auto buf = static_cast<double *>(calloc(input.len, sizeof(double)));
EXPECT_TRUE(buf != nullptr);
memcpy(buf, input.buf, input.len * sizeof(double));
result->input.len = input.len;
result->input.cap = input.len;
result->input.buf = buf;
*out = result;
return 0;
}
}
TEST_F(JsonAvprRpcTests, output) {
dyn_interface_type * intf = dynInterface_parseAvprWithStr(sourceAvprFile);
ASSERT_TRUE(intf != nullptr);
char *result = nullptr;
tst_serv serv {nullptr, nullptr, nullptr, nullptr, xstats};
int rc = jsonRpc_call(intf, &serv, R"({"m":"stats", "a": [[1.0, 2.0]]})", &result);
ASSERT_EQ(0, rc);
ASSERT_TRUE(strstr(result, "1.5") != nullptr);
free(result);
dynInterface_destroy(intf);
}
// Test char array as output
extern "C" {
struct tst_service_ex_output_char {
void *handle;
int (*getName_x)(void *, char ** result);
};
int getName_ex(void*, char** result) {
*result = strdup("allocatedInFunction");
return 0;
}
}
TEST_F(JsonAvprRpcTests, output_char) {
dyn_interface_type * intf = dynInterface_parseAvprWithStr(sourceAvprFile);
ASSERT_TRUE(intf != nullptr);
char *result = nullptr;
tst_service_ex_output_char serv {nullptr, getName_ex};
int rc = jsonRpc_call(intf, &serv, R"({"m" : "getName", "a": []})", &result);
ASSERT_EQ(0, rc);
ASSERT_TRUE(strstr(result, "allocatedInFunction") != nullptr);
free(result);
dynInterface_destroy(intf);
}