Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
testjsonipc.cc
Go to the documentation of this file.
1 // Dedicated to the Public Domain under the Unlicense: https://unlicense.org/UNLICENSE
2#include "jsonipc.hh"
3#include <iostream>
4
5#define MCHECK(MSG, ...) do { if (!strstr (MSG.c_str(), "\"error\":")) break; fprintf (stderr, "%s:%d: ERROR: %s\n", __FILE__, __LINE__, MSG.c_str()); return __VA_ARGS__; } while (0)
6
7// == Testing ==
8enum ErrorType {
9 NONE,
10 INVALID,
11 FATAL
12};
13
14struct Copyable {
15 int i = 111;
16 float f = -0.05;
17 std::string hello = "hello";
18};
19
20struct Base {
21 Base() = default;
22 Base(const Base&) = default;
23 void need_copyablep (std::shared_ptr<Copyable> cp) { JSONIPC_ASSERT_RETURN (cp); }
24 virtual ~Base() {}
25};
27struct Base2 {
28 Base2() = default;
29 Base2(const Base2&) = default;
30 virtual ~Base2() {}
31 Copyable randomize() { Copyable c; c.i = rand(); c.f = rand() / 10.0; return c; }
32};
36 const std::string name_;
38 Derived (const std::string &name) : name_ (name) {}
39 void dummy0 () { printf ("dummy0: NOP\n"); }
40 bool dummy1 (bool b, KVInts kvi) { printf ("dummy1: b=%s kvi=%zd\n", b ? "true" : "false", kvi.size()); return b; }
41 Pair dummy2 (std::string s, int i) { printf ("dummy2: s=\"%s\" i=%d\n", s.c_str(), i); return {}; }
42 size_t dummy3 (Derived &d, KVFloats kvf) const { printf ("dummy3: Derived=%p this=%p kvf=%zd\n", &d, this, kvf.size()); return size_t (&d); }
43 bool dummy4 (float f, std::string s, long l) { printf ("dummy4: this=%s %f '%s' %ld\n", name_.c_str(), f, s.c_str(), l); return 1; }
44 void dummy5 (const char *c, double d, Pair p) { printf ("dummy5: this=%s %d '%s' %f\n", name_.c_str(), p.first, c, d); }
45 std::string dummy6 (int, const std::string &) { return ""; }
46 Derived* dummy7 () { return NULL; }
47 Derived& dummy8 () { return *dummy7(); }
48 Derived dummy9 () { return dummy8(); }
49 void defaults (bool a1 = true, ErrorType a2 = ErrorType::FATAL, std::string a3 = std::string ("a3"),
50 signed a4 = -4, float a5 = -0.5, const char *a6 = "a6", double a7 = 0.7,
51 size_t a8 = 8, Copyable *a9 = nullptr,
52 Base *b = nullptr) {} // see .set_dflt()
53 template<class C> static void
54 set_dflt (C &c) // see .defaults()
55 {
56 c.set_d ("defaults", &Derived::defaults, {
57 true, ErrorType::FATAL, std::string ("a3"),
58 signed (-4), float (-0.5), "a6", 1e-70,
59 size_t (8), nullptr,
60 {} // not possible: (Base*) nullptr,
61 });
62 }
63};
64
65static size_t
66json_objectid (const Jsonipc::JsonValue &value)
67{
68 if (value.IsObject())
69 {
70 auto it = value.FindMember ("$id");
71 if (it != value.MemberEnd())
72 return Jsonipc::from_json<size_t> (it->value);
73 }
74 return 0;
75}
76
77template<typename R> R
78parse_result (size_t id, const std::string json_reply)
79{
80 rapidjson::Document document;
81 document.Parse<Jsonipc::rapidjson_parse_flags> (json_reply.data(), json_reply.size());
82 if (!document.HasParseError())
83 {
84 size_t id_ = 0;
85 const Jsonipc::JsonValue *result = NULL;
86 for (const auto &m : document.GetObject())
87 if (m.name == "id")
88 id_ = Jsonipc::from_json<size_t> (m.value, 0);
89 else if (m.name == "result")
90 result = &m.value;
91 if (id_ == id && result)
92 return Jsonipc::from_json<R> (*result);
93 }
94 return R();
95}
96
97static void
98test_jsonipc (bool dispatcher_shell, bool printer)
99{
100 using namespace Jsonipc;
101 if (printer)
102 g_binding_printer = new BindingPrinter();
103 rapidjson::Document doc;
104 auto &a = doc.GetAllocator();
105
106 // test basics
107 JSONIPC_ASSERT_RETURN (false == from_json<bool> (JsonValue()));
108 JSONIPC_ASSERT_RETURN (true == from_json<bool> (JsonValue (true)));
109 JSONIPC_ASSERT_RETURN (true == from_json<bool> (JsonValue(), true));
110 JSONIPC_ASSERT_RETURN (false == from_json<bool> (JsonValue(), false));
111 JSONIPC_ASSERT_RETURN (from_json<bool> (to_json (true, a)) == true);
112 JSONIPC_ASSERT_RETURN (from_json<bool> (to_json (false, a)) == false);
113 JSONIPC_ASSERT_RETURN (from_json<size_t> (to_json (1337, a)) == 1337);
114 JSONIPC_ASSERT_RETURN (from_json<ssize_t> (to_json (-1337, a)) == -1337);
115 JSONIPC_ASSERT_RETURN (from_json<float> (to_json (-0.5, a)) == -0.5);
116 JSONIPC_ASSERT_RETURN (from_json<double> (to_json (1e20, a)) == 1e20);
117 JSONIPC_ASSERT_RETURN (from_json<const char*> (to_json ("Om", a)) == std::string ("Om"));
118 JSONIPC_ASSERT_RETURN (from_json<std::string> (to_json (std::string ("Ah"), a)) == "Ah");
119 JSONIPC_ASSERT_RETURN (strcmp ("HUM", from_json<const char*> (to_json ((const char*) "HUM", a))) == 0);
120
121 // register test classes and methods
122 Jsonipc::Enum<ErrorType> enum_ErrorType;
123 enum_ErrorType
124 .set (ErrorType::NONE, "NONE")
125 .set (ErrorType::INVALID, "INVALID")
126 .set (ErrorType::FATAL, "FATAL")
127 ;
128
129 Jsonipc::Class<Base> class_Base;
130 class_Base
131 .set ("need_copyablep", &Base::need_copyablep)
132 ;
133 Jsonipc::Class<Base2> class_Base2;
134 Jsonipc::Serializable<Copyable> class_Copyable;
135 class_Copyable
136 // .copy ([] (const Copyable &o) { return std::make_shared<std::decay<decltype (o)>::type> (o); })
137 .set ("i", &Copyable::i)
138 .set ("f", &Copyable::f)
139 .set ("hello", &Copyable::hello)
140 ;
141 Jsonipc::Class<Derived> class_Derived;
142 class_Derived
143 .inherit<Base>()
144 .inherit<Base2>()
145 .set ("dummy0", &Derived::dummy0)
146 .set ("dummy1", &Derived::dummy1)
147 .set ("dummy2", &Derived::dummy2)
148 .set ("dummy3", &Derived::dummy3)
149 .set ("dummy4", &Derived::dummy4)
150 .set ("dummy5", &Derived::dummy5)
151 .set ("dummy6", &Derived::dummy6)
152 .set ("dummy7", &Derived::dummy7)
153 .set ("dummy8", &Derived::dummy8)
154 .set ("dummy9", &Derived::dummy9)
155 .set ("randomize", &Derived::randomize)
156 ;
157 Derived::set_dflt (class_Derived);
158
159 // Provide scope and instance ownership during dispatch_message()
160 InstanceMap imap;
161 Scope temporary_scope (imap); // needed by to_/from_json and dispatcher
162
163 // test bindings
164 auto objap = std::make_shared<Derived> ("obja");
165 Derived &obja = *objap;
166 JSONIPC_ASSERT_RETURN (to_json (obja, a) == to_json (obja, a));
167 JSONIPC_ASSERT_RETURN (&obja == from_json<Derived*> (to_json (obja, a)));
168 JSONIPC_ASSERT_RETURN (ptrdiff_t (&static_cast<Base&> (obja)) == ptrdiff_t (&obja));
169 JSONIPC_ASSERT_RETURN (ptrdiff_t (&static_cast<Base2&> (obja)) > ptrdiff_t (&obja));
170 // given the same id, Base and Base2 need to unwrap to different addresses due to multiple inheritance
171 JSONIPC_ASSERT_RETURN (&obja == from_json<Base*> (to_json (obja, a)));
172 JSONIPC_ASSERT_RETURN (from_json<Base2*> (to_json (obja, a)) == &static_cast<Base2&> (obja));
173 auto objbp = std::make_shared<Derived> ("objb");
174 Derived &objb = *objbp;
175 JSONIPC_ASSERT_RETURN (&obja != &objb);
176 // const size_t idb = class_Derived.wrap_object (objb);
177 JSONIPC_ASSERT_RETURN (from_json<std::shared_ptr<Derived>> (to_json (objb, a)).get() == &objb);
178 JSONIPC_ASSERT_RETURN (ptrdiff_t (&static_cast<Base&> (objb)) == ptrdiff_t (&objb));
179 JSONIPC_ASSERT_RETURN (ptrdiff_t (&static_cast<Base2&> (objb)) > ptrdiff_t (&objb));
180 JSONIPC_ASSERT_RETURN (from_json<std::shared_ptr<Base>> (to_json (objb, a)).get() == &static_cast<Base&> (objb));
181 JSONIPC_ASSERT_RETURN (from_json<std::shared_ptr<Base2>> (to_json (objb, a)).get() == &static_cast<Base2&> (objb));
182 JSONIPC_ASSERT_RETURN (to_json (obja, a) != to_json (objb, a));
183 auto objcp = std::make_shared<Derived> ("objc");
184 Derived &objc = *objcp;
185 JSONIPC_ASSERT_RETURN (&objc == &from_json<Derived&> (to_json (objc, a)));
186 JSONIPC_ASSERT_RETURN (&objc == &from_json<Base&> (to_json (objc, a)));
187 JSONIPC_ASSERT_RETURN (&objc == &from_json<Base2&> (to_json (objc, a)));
188 JSONIPC_ASSERT_RETURN (to_json (objb, a) != to_json (objc, a));
189 JSONIPC_ASSERT_RETURN (to_json (objc, a) == to_json (objc, a));
190 const JsonValue jva = to_json (obja, a);
191 const JsonValue jvb = to_json (objb, a);
192 const JsonValue jvc = to_json (objc, a);
193 JSONIPC_ASSERT_RETURN (from_json<Derived*> (jva) == &obja);
194 JSONIPC_ASSERT_RETURN (&from_json<Derived> (jvb) == &objb);
195 JSONIPC_ASSERT_RETURN (&from_json<Derived&> (jvc) == &objc);
196
197 // Serializable tests
198 std::string result;
199 Copyable c1 { 2345, -0.5, "ehlo" };
200 JsonValue jvc1 = to_json (c1, a);
201 Copyable c2 = from_json<Copyable&> (jvc1);
202 JSONIPC_ASSERT_RETURN (c1.i == c2.i && c1.f == c2.f && c1.hello == c2.hello);
203
204 // dispatcher tests
205 IpcDispatcher dispatcher;
206 auto d1p = std::make_shared<Derived> ("dood");
207 Derived &d1 = *d1p;
208 JsonValue jvd1 = to_json (d1, a);
209 const size_t d1id = json_objectid (jvd1);
210 JSONIPC_ASSERT_RETURN (d1id == 4); // used in the next few lines
211 result = dispatcher.dispatch_message (R"( {"id":123,"method":"randomize","params":[{"$id":4}]} )");
212 MCHECK (result);
213 const Copyable c0;
214 const Copyable *c3 = parse_result<Copyable*> (123, result);
215 JSONIPC_ASSERT_RETURN (c3 && (c3->i != c0.i || c3->f != c0.f));
216 result = dispatcher.dispatch_message (R"( {"id":123,"method":"need_copyablep","params":[{"$id":4},{}]} )");
217 MCHECK (result);
218 result = dispatcher.dispatch_message (R"( {"id":444,"method":"randomize","params":[{"$id":4}]} )");
219 MCHECK (result);
220 const Copyable *c4 = parse_result<Copyable*> (444, result);
221 JSONIPC_ASSERT_RETURN (c4 && (c4->i != c3->i || c4->f != c3->f));
222 result = dispatcher.dispatch_message (R"( {"id":111,"method":"randomize","params":[{"$id":4}]} )");
223 MCHECK (result);
224 const Copyable *c5 = parse_result<Copyable*> (111, result);
225 JSONIPC_ASSERT_RETURN (c5 && (c5->i != c4->i || c5->f != c4->f));
226
227 if (printer && Jsonipc::g_binding_printer) {
228 dprintf (1, "%s\n", Jsonipc::g_binding_printer->finish().c_str());
229 }
230
231 // CLI test server
232 if (dispatcher_shell)
233 {
234 for (std::string line; std::getline (std::cin, line); )
235 std::cout << dispatcher.dispatch_message (line) << std::endl;
236 // Feed example lines:
237 // {"id":123,"method":"dummy3","params":[2],"this":1}
238 }
239
240 // unregister thisids for objects living on the stack
241 forget_json_id (json_objectid (jva));
242 JSONIPC_ASSERT_RETURN (from_json<Derived*> (jva) == nullptr);
243 forget_json_id (json_objectid (jvb));
244 JSONIPC_ASSERT_RETURN (from_json<Derived*> (jvb) == (Derived*) nullptr);
245 forget_json_id (json_objectid (jvc));
246 JSONIPC_ASSERT_RETURN (from_json<Derived*> (jvc) == (Derived*) nullptr);
247
248 printf (" OK %s\n", __func__);
249}
250
251#ifdef STANDALONE
252int
253main (int argc, char *argv[])
254{
255 const bool dispatcher_shell = argc > 1 && 0 == strcmp (argv[1], "--shell");
256 const bool printer = argc > 1 && 0 == strcmp (argv[1], "--print");
257 test_jsonipc (dispatcher_shell, printer);
258 return 0;
259}
260#endif
T c_str(T... args)
Keep track of temporary instances during IpcDispatcher::dispatch_message().
Definition jsonipc.hh:163
T data(T... args)
printf
T endl(T... args)
T getline(T... args)
typedef float
rand
T size(T... args)
strcmp
Class & set(const char *name, const F &method)
Add a member function pointer.
Definition jsonipc.hh:1300
Jsonipc wrapper type for objects that support field-wise serialization to/from JSON.
Definition jsonipc.hh:1185
Serializable & set(const char *name, A attribute)
Add a member object pointer.
Definition jsonipc.hh:1195
typedef size_t