Anklang-0.3.0-1416-gfa8ce8d2 anklang-0.3.0-1416-gfa8ce8d2
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()); abort(); } 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
21 Copyable nested;
22};
23
27
28struct Base {
29 Base() = default;
30 Base(const Base&) = default;
31 void need_copyablep (std::shared_ptr<Copyable> cp) { JSONIPC_ASSERT_RETURN (cp); }
32 virtual ~Base() {}
33};
35struct Base2 {
36 Base2() = default;
37 Base2(const Base2&) = default;
38 virtual ~Base2() {}
39 Copyable randomize() { Copyable c; c.i = rand(); c.f = rand() / 10.0; return c; }
40};
44 const std::string name_;
45 NestedCopyable nested_;
48 Derived (const std::string &name) : name_ (name) {}
49 NestedCopyable nested() const { return nested_; }
50 void set_nested (const NestedCopyable &nested) { nested_ = nested; }
51 const std::shared_ptr<LinkedCopyable>& linked() const { return linked_; }
52 void set_linked (const std::shared_ptr<LinkedCopyable> &linked) { linked_ = linked; }
53 void dummy0 () { printf ("dummy0: NOP\n"); }
54 bool dummy1 (bool b, KVInts kvi) { printf ("dummy1: b=%s kvi=%zd\n", b ? "true" : "false", kvi.size()); return b; }
55 Pair dummy2 (std::string s, int i) { printf ("dummy2: s=\"%s\" i=%d\n", s.c_str(), i); return {}; }
56 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); }
57 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; }
58 void dummy5 (const char *c, double d, Pair p) { printf ("dummy5: this=%s %d '%s' %f\n", name_.c_str(), p.first, c, d); }
59 std::string dummy6 (int, const std::string &) { return ""; }
60 Derived* dummy7 () { return NULL; }
61 Derived& dummy8 () { return *dummy7(); }
62 Derived dummy9 () { return dummy8(); }
63 void defaults (bool a1 = true, ErrorType a2 = ErrorType::FATAL, std::string a3 = std::string ("a3"),
64 signed a4 = -4, float a5 = -0.5, const char *a6 = "a6", double a7 = 0.7,
65 size_t a8 = 8, Copyable *a9 = nullptr,
66 Base *b = nullptr) {} // see .set_dflt()
67 template<class C> static void
68 set_dflt (C &c) // see .defaults()
69 {
70 c.set_d ("defaults", &Derived::defaults, {
71 true, ErrorType::FATAL, std::string ("a3"),
72 signed (-4), float (-0.5), "a6", 1e-70,
73 size_t (8), nullptr,
74 {} // not possible: (Base*) nullptr,
75 });
76 }
77};
78
79static size_t
80json_objectid (const Jsonipc::JsonValue &value)
81{
82 if (value.IsObject())
83 {
84 auto it = value.FindMember ("$id");
85 if (it != value.MemberEnd())
86 return Jsonipc::from_json<size_t> (it->value);
87 }
88 return 0;
89}
90
91template<typename R> R
92parse_result (size_t id, const std::string json_reply)
93{
94 rapidjson::Document document;
95 document.Parse<Jsonipc::rapidjson_parse_flags> (json_reply.data(), json_reply.size());
96 if (!document.HasParseError())
97 {
98 size_t id_ = 0;
99 const Jsonipc::JsonValue *result = NULL;
100 for (const auto &m : document.GetObject())
101 if (m.name == "id")
102 id_ = Jsonipc::from_json<size_t> (m.value, 0);
103 else if (m.name == "result")
104 result = &m.value;
105 if (id_ == id && result)
106 return Jsonipc::from_json<R> (*result);
107 }
108 return R();
109}
110
111static void
112test_jsonipc (bool dispatcher_shell, bool printer)
113{
114 using namespace Jsonipc;
115 if (printer)
116 g_binding_printer = new BindingPrinter();
117 rapidjson::Document doc;
118 auto &a = doc.GetAllocator();
119
120 // test basics
121 JSONIPC_ASSERT_RETURN (false == from_json<bool> (JsonValue()));
122 JSONIPC_ASSERT_RETURN (true == from_json<bool> (JsonValue (true)));
123 JSONIPC_ASSERT_RETURN (true == from_json<bool> (JsonValue(), true));
124 JSONIPC_ASSERT_RETURN (false == from_json<bool> (JsonValue(), false));
125 JSONIPC_ASSERT_RETURN (from_json<bool> (to_json (true, a)) == true);
126 JSONIPC_ASSERT_RETURN (from_json<bool> (to_json (false, a)) == false);
127 JSONIPC_ASSERT_RETURN (from_json<size_t> (to_json (1337, a)) == 1337);
128 JSONIPC_ASSERT_RETURN (from_json<ssize_t> (to_json (-1337, a)) == -1337);
129 JSONIPC_ASSERT_RETURN (from_json<float> (to_json (-0.5, a)) == -0.5);
130 JSONIPC_ASSERT_RETURN (from_json<double> (to_json (1e20, a)) == 1e20);
131 JSONIPC_ASSERT_RETURN (from_json<const char*> (to_json ("Om", a)) == std::string ("Om"));
132 JSONIPC_ASSERT_RETURN (from_json<std::string> (to_json (std::string ("Ah"), a)) == "Ah");
133 JSONIPC_ASSERT_RETURN (strcmp ("HUM", from_json<const char*> (to_json ((const char*) "HUM", a))) == 0);
134
135 // register test classes and methods
136 Jsonipc::Enum<ErrorType> enum_ErrorType;
137 enum_ErrorType
138 .set (ErrorType::NONE, "NONE")
139 .set (ErrorType::INVALID, "INVALID")
140 .set (ErrorType::FATAL, "FATAL")
141 ;
142
143 Jsonipc::Class<Base> class_Base;
144 class_Base
145 .set ("need_copyablep", &Base::need_copyablep)
146 ;
147 Jsonipc::Class<Base2> class_Base2;
148 Jsonipc::Serializable<NestedCopyable> class_NestedCopyable;
149 class_NestedCopyable.set ("nested", &NestedCopyable::nested);
150 Jsonipc::Serializable<Copyable> class_Copyable;
151 class_Copyable
152 // .copy ([] (const Copyable &o) { return std::make_shared<std::decay<decltype (o)>::type> (o); })
153 .set ("i", &Copyable::i)
154 .set ("f", &Copyable::f)
155 .set ("hello", &Copyable::hello)
156 ;
157 Jsonipc::Serializable<LinkedCopyable> class_LinkedCopyable;
158 class_LinkedCopyable.set ("next", &LinkedCopyable::next);
159 Jsonipc::Class<Derived> class_Derived;
160 class_Derived
161 .inherit<Base>()
162 .set ("dummy0", &Derived::dummy0)
163 .set ("dummy1", &Derived::dummy1)
164 .set ("dummy2", &Derived::dummy2)
165 .set ("dummy3", &Derived::dummy3)
166 .set ("dummy4", &Derived::dummy4)
167 .set ("dummy5", &Derived::dummy5)
168 .set ("dummy6", &Derived::dummy6)
169 .set ("dummy7", &Derived::dummy7)
170 .set ("dummy8", &Derived::dummy8)
171 .set ("dummy9", &Derived::dummy9)
172 .set ("randomize", &Derived::randomize)
173 .set ("nested", &Derived::nested, &Derived::set_nested)
174 .set ("linked", &Derived::linked, &Derived::set_linked)
175 .inherit<Base2>()
176 ;
177 Derived::set_dflt (class_Derived);
178 using Names = std::vector<std::string>;
179 JSONIPC_ASSERT_RETURN (Class<Base>::list_methods() == Names { "need_copyablep" });
180 const Names derived_methods = { "defaults", "dummy0", "dummy1", "dummy2", "dummy3", "dummy4", "dummy5", "dummy6", "dummy7",
181 "dummy8", "dummy9", "get/linked", "get/nested", "need_copyablep", "randomize",
182 "set/linked", "set/nested" };
183 JSONIPC_ASSERT_RETURN (Class<Derived>::list_methods() == derived_methods);
184
185 // Provide scope and instance ownership during dispatch_message()
186 InstanceMap imap;
187 Scope temporary_scope (imap); // needed by to_/from_json and dispatcher
188 const auto nested = from_json<NestedCopyable> (to_json (NestedCopyable{}, a));
189 JSONIPC_ASSERT_RETURN (nested.nested.i == 111 && nested.nested.hello == "hello");
190
191 // test bindings
192 auto objap = std::make_shared<Derived> ("obja");
193 Derived &obja = *objap;
194 JSONIPC_ASSERT_RETURN (to_json (obja, a) == to_json (obja, a));
195 JSONIPC_ASSERT_RETURN (&obja == from_json<Derived*> (to_json (obja, a)));
196 JSONIPC_ASSERT_RETURN (ptrdiff_t (&static_cast<Base&> (obja)) == ptrdiff_t (&obja));
197 JSONIPC_ASSERT_RETURN (ptrdiff_t (&static_cast<Base2&> (obja)) > ptrdiff_t (&obja));
198 // given the same id, Base and Base2 need to unwrap to different addresses due to multiple inheritance
199 JSONIPC_ASSERT_RETURN (&obja == from_json<Base*> (to_json (obja, a)));
200 JSONIPC_ASSERT_RETURN (from_json<Base2*> (to_json (obja, a)) == &static_cast<Base2&> (obja));
201 auto objbp = std::make_shared<Derived> ("objb");
202 Derived &objb = *objbp;
203 JSONIPC_ASSERT_RETURN (&obja != &objb);
204 // const size_t idb = class_Derived.wrap_object (objb);
205 JSONIPC_ASSERT_RETURN (from_json<std::shared_ptr<Derived>> (to_json (objb, a)).get() == &objb);
206 JSONIPC_ASSERT_RETURN (ptrdiff_t (&static_cast<Base&> (objb)) == ptrdiff_t (&objb));
207 JSONIPC_ASSERT_RETURN (ptrdiff_t (&static_cast<Base2&> (objb)) > ptrdiff_t (&objb));
208 JSONIPC_ASSERT_RETURN (from_json<std::shared_ptr<Base>> (to_json (objb, a)).get() == &static_cast<Base&> (objb));
209 JSONIPC_ASSERT_RETURN (from_json<std::shared_ptr<Base2>> (to_json (objb, a)).get() == &static_cast<Base2&> (objb));
210 JSONIPC_ASSERT_RETURN (to_json (obja, a) != to_json (objb, a));
211 auto objcp = std::make_shared<Derived> ("objc");
212 Derived &objc = *objcp;
213 JSONIPC_ASSERT_RETURN (&objc == &from_json<Derived&> (to_json (objc, a)));
214 JSONIPC_ASSERT_RETURN (&objc == &from_json<Base&> (to_json (objc, a)));
215 JSONIPC_ASSERT_RETURN (&objc == &from_json<Base2&> (to_json (objc, a)));
216 JSONIPC_ASSERT_RETURN (to_json (objb, a) != to_json (objc, a));
217 JSONIPC_ASSERT_RETURN (to_json (objc, a) == to_json (objc, a));
218 const JsonValue jva = to_json (obja, a);
219 const JsonValue jvb = to_json (objb, a);
220 const JsonValue jvc = to_json (objc, a);
221 JSONIPC_ASSERT_RETURN (from_json<Derived*> (jva) == &obja);
222 JSONIPC_ASSERT_RETURN (&from_json<Derived> (jvb) == &objb);
223 JSONIPC_ASSERT_RETURN (&from_json<Derived&> (jvc) == &objc);
224
225 // Serializable tests
226 std::string result;
227 Copyable c1 { 2345, -0.5, "ehlo" };
228 JsonValue jvc1 = to_json (c1, a);
229 Copyable c2 = from_json<Copyable&> (jvc1);
230 JSONIPC_ASSERT_RETURN (c1.i == c2.i && c1.f == c2.f && c1.hello == c2.hello);
231
232 // dispatcher tests
233 IpcDispatcher dispatcher;
234 auto d1p = std::make_shared<Derived> ("dood");
235 Derived &d1 = *d1p;
236 JsonValue jvd1 = to_json (d1, a);
237 const size_t d1id = json_objectid (jvd1);
238 JSONIPC_ASSERT_RETURN (d1id == 4); // used in the next few lines
239 JSONIPC_ASSERT_RETURN (InstanceMap::scope_lookup_wrapper (jvd1)->list_methods() == derived_methods);
240 result = dispatcher.dispatch_message (R"( {"id":123,"method":"randomize","params":[{"$id":4}]} )");
241 MCHECK (result);
242 const Copyable c0;
243 const Copyable *c3 = parse_result<Copyable*> (123, result);
244 JSONIPC_ASSERT_RETURN (c3 && (c3->i != c0.i || c3->f != c0.f));
245 result = dispatcher.dispatch_message (R"( {"id":123,"method":"need_copyablep","params":[{"$id":4},{}]} )");
246 MCHECK (result);
247 result = dispatcher.dispatch_message (R"( {"id":444,"method":"randomize","params":[{"$id":4}]} )");
248 MCHECK (result);
249 const Copyable *c4 = parse_result<Copyable*> (444, result);
250 JSONIPC_ASSERT_RETURN (c4 && (c4->i != c3->i || c4->f != c3->f));
251 result = dispatcher.dispatch_message (R"( {"id":111,"method":"randomize","params":[{"$id":4}]} )");
252 MCHECK (result);
253 const Copyable *c5 = parse_result<Copyable*> (111, result);
254 JSONIPC_ASSERT_RETURN (c5 && (c5->i != c4->i || c5->f != c4->f));
255
256 if (printer && Jsonipc::g_binding_printer) {
257 const auto bindings = Jsonipc::g_binding_printer->finish();
258 std::string missing;
259 for (const auto *expected : { "nested: Copyable = new Copyable()", "next: LinkedCopyable = null",
260 "this.$get (\"nested\", new NestedCopyable())", "this.$get (\"linked\", null)" })
261 if (bindings.find (expected) == std::string::npos)
262 missing += std::string (expected) + "\n";
263 if (!missing.empty())
264 throw std::runtime_error ("Missing record defaults:\n" + missing);
265 dprintf (1, "%s\n", bindings.c_str());
266 }
267
268 // CLI test server
269 if (dispatcher_shell)
270 {
271 for (std::string line; std::getline (std::cin, line); )
272 std::cout << dispatcher.dispatch_message (line) << std::endl;
273 // Feed example lines:
274 // {"id":123,"method":"dummy3","params":[2],"this":1}
275 }
276
277 // unregister thisids for objects living on the stack
278 forget_json_id (json_objectid (jva));
279 JSONIPC_ASSERT_RETURN (from_json<Derived*> (jva) == nullptr);
280 forget_json_id (json_objectid (jvb));
281 JSONIPC_ASSERT_RETURN (from_json<Derived*> (jvb) == (Derived*) nullptr);
282 forget_json_id (json_objectid (jvc));
283 JSONIPC_ASSERT_RETURN (from_json<Derived*> (jvc) == (Derived*) nullptr);
284
285 printf (" OK %s\n", __func__);
286}
287
288#ifdef STANDALONE
289int
290main (int argc, char *argv[])
291{
292 const bool dispatcher_shell = argc > 1 && 0 == strcmp (argv[1], "--shell");
293 const bool printer = argc > 1 && 0 == strcmp (argv[1], "--print");
294 test_jsonipc (dispatcher_shell, printer);
295 return 0;
296}
297#endif
T c_str(T... args)
Maps C++ shared_ptr instances to JSON-wrapped objects with unique IDs, supporting polymorphic upcasti...
Definition jsonipc.hh:539
Keep track of temporary instances during IpcDispatcher::dispatch_message().
Definition jsonipc.hh:163
T data(T... args)
printf
T empty(T... args)
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:1329
Registers C++ enum values with string names for JSON serialization and TypeScript binding generation.
Definition jsonipc.hh:1111
Jsonipc wrapper type for objects that support field-wise serialization to/from JSON.
Definition jsonipc.hh:1214
Serializable & set(const char *name, A attribute)
Add a member object pointer.
Definition jsonipc.hh:1224
typedef size_t