p≡p JSON adapter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

231 lines
7.3 KiB

  1. #include <gtest/gtest.h>
  2. #include "json_rpc.hh"
  3. #include "json-adapter.hh"
  4. #include "function_map.hh"
  5. #include "c_string.hh"
  6. #include "pEp-types.hh"
  7. #include "session_registry.hh"
  8. #include "json_spirit/json_spirit_reader.h"
  9. #include <pEp/pEp_string.h> // for new_string()
  10. #include <pEp/stringlist.h>
  11. #include <vector>
  12. namespace js = json_spirit;
  13. namespace json_spirit
  14. {
  15. std::ostream& operator<<(std::ostream& os, const Value& value)
  16. {
  17. js::write(value, os, js::pretty_print | js::raw_utf8 );
  18. return os;
  19. }
  20. std::ostream& operator<<(std::ostream& os, const Object& obj)
  21. {
  22. js::write(obj, os, 0x1B, 0);
  23. return os;
  24. }
  25. } // end of namespace json_spirit
  26. namespace {
  27. // HACK: Define a dummy type, so I can define an operator<< which is used by GTest,
  28. // which refuses to pretty-print js::Object directly, for whatever reason. *sigh*
  29. struct O
  30. {
  31. js::Object obj;
  32. };
  33. std::ostream& operator<<(std::ostream& os, const O& o)
  34. {
  35. js::write(o.obj, os, 0x1B, 0);
  36. return os;
  37. }
  38. bool operator==(const O& o1, const O& o2)
  39. {
  40. return o1.obj == o2.obj;
  41. }
  42. /// END OF HACK
  43. class DummyAdapter : public JsonAdapterBase
  44. {
  45. public:
  46. DummyAdapter()
  47. : sr{nullptr, nullptr, 4}
  48. {}
  49. virtual bool verify_security_token(const std::string& token) const override { return true; }
  50. virtual void cache(const std::string& client_id, const std::string& func_name, const std::function<void(PEP_SESSION)>& fn) override
  51. {
  52. sr.add_to_cache(client_id, func_name, fn);
  53. }
  54. private:
  55. SessionRegistry sr;
  56. };
  57. // some example & test functions:
  58. int add_mul_simple(int x, int y, int z)
  59. {
  60. return (x+y) * z;
  61. }
  62. // test function for InOut parameters etc.
  63. char* add_mul_inout(int x, const char* y_str, int* z_result, char** result)
  64. {
  65. const int y = y_str ? strtol(y_str, nullptr, 0) : -1;
  66. const int z = z_result ? *z_result : -1;
  67. const int r = (x+y) * z;
  68. if(z_result)
  69. *z_result = r;
  70. const std::string rs = std::to_string(r);
  71. char* rcs = new_string( rs.c_str(), 0 ); // == strdup() but allocated on Engine's heap
  72. *result = rcs;
  73. return new_string( ("x" + rs + "x").c_str(), 0);
  74. }
  75. char* tohex(const char* input, size_t length)
  76. {
  77. std::string h; h.reserve(length*3);
  78. char buffer[8] = { 0 };
  79. const char* end = input+length;
  80. for(; input<end; ++input)
  81. {
  82. snprintf(buffer,7, "%02hhx", (unsigned char)*input );
  83. if(!h.empty()) h += ' ';
  84. h += buffer;
  85. }
  86. return new_string( h.c_str(), 0 );
  87. }
  88. js::Array gen_array(size_t num_elements)
  89. {
  90. js::Array a;
  91. for(unsigned u=0; u<num_elements; ++u)
  92. {
  93. a.push_back( js::Value( static_cast<int>(u)) );
  94. }
  95. return a;
  96. }
  97. const FunctionMap test_functions = {
  98. FP( "add_mul_simple", new Func<int, In<int>, In<int>, In<int>>( &add_mul_simple )),
  99. FP( "add_mul_inout" , new Func<char*, In<int>, In<c_string>, InOutP<int>, Out<char*>>( &add_mul_inout )),
  100. FP( "stringlist_add", new Func<Out<stringlist_t*, ParamFlag::DontOwn>, InOut<stringlist_t*>, In<c_string>>( &stringlist_add )),
  101. FP( "tohex_1", new Func<char*, In<c_string>, In<size_t>>( &tohex )), // with explicit length parameter
  102. FP( "tohex_2", new Func<char*, In<c_string>, InLength<>>( &tohex )), // with implicit length parameter, with dummy JSON parameter
  103. FP( "tohex_3", new Func<char*, In<c_string>, InLength<ParamFlag::NoInput>>( &tohex )), // with implicit length parameter, without JSON parameter
  104. FP( "gen_array", new Func<js::Array, In<size_t>>( &gen_array )),
  105. // TODO: test FuncCache stuff, too. :-/
  106. // FP( "cache_s1", new FuncCache<void, In_Pep_Session, In<c_string>> ( "cache_s1", &cache_s1 )),
  107. };
  108. struct TestEntry
  109. {
  110. std::string input;
  111. std::string result;
  112. };
  113. std::ostream& operator<<(std::ostream& o, const TestEntry& tt)
  114. {
  115. return o << "input=«" << tt.input << "», result=«" << tt.result << "». ";
  116. }
  117. const std::vector<TestEntry> testValues =
  118. {
  119. { "{\"jsonrpc\":\"2.0\", \"id\":21, \"method\":\"add_mul_simple\", \"params\":[10,11,12]}",
  120. "{\"jsonrpc\":\"2.0\", \"id\":21, \"result\":{ \"outParams\":[], \"return\":252}}"
  121. },
  122. { "{\"jsonrpc\":\"2.0\", \"id\":22, \"method\":\"add_mul_simple\", \"params\":[10,-11,-12]}",
  123. "{\"jsonrpc\":\"2.0\", \"id\":22, \"result\":{ \"outParams\":[], \"return\":12}}"
  124. },
  125. { "{\"jsonrpc\":\"2.0\", \"id\":23, \"method\":\"add_mul_inout\", \"params\":[100,\"111\",123, \"dummy\"]}",
  126. "{\"jsonrpc\":\"2.0\", \"id\":23, \"result\":{ \"outParams\":[\"25953\",25953], \"return\":\"x25953x\"}}"
  127. },
  128. { "{\"jsonrpc\":\"2.0\", \"id\":24, \"method\":\"stringlist_add\", \"params\":[[\"abc\",\"def\"], \"ADD\"]}",
  129. "{\"jsonrpc\":\"2.0\", \"id\":24, \"result\":{ \"outParams\":[[\"abc\", \"def\", \"ADD\"]], \"return\":[\"ADD\"]}}"
  130. },
  131. // tohex:
  132. { "{\"jsonrpc\":\"2.0\", \"id\":25, \"method\":\"tohex_1\", \"params\":[\"tohex\",3]}",
  133. "{\"jsonrpc\":\"2.0\", \"id\":25, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68\"}}"
  134. },
  135. { "{\"jsonrpc\":\"2.0\", \"id\":26, \"method\":\"tohex_1\", \"params\":[\"tohex\",5]}",
  136. "{\"jsonrpc\":\"2.0\", \"id\":26, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68 65 78\"}}"
  137. },
  138. { "{\"jsonrpc\":\"2.0\", \"id\":27, \"method\":\"tohex_2\", \"params\":[\"tohex\",0]}",
  139. "{\"jsonrpc\":\"2.0\", \"id\":27, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68 65 78\"}}"
  140. },
  141. { "{\"jsonrpc\":\"2.0\", \"id\":28, \"method\":\"tohex_2\", \"params\":[\"tohex\",\"dummy_parameter\"]}",
  142. "{\"jsonrpc\":\"2.0\", \"id\":28, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68 65 78\"}}"
  143. },
  144. { "{\"jsonrpc\":\"2.0\", \"id\":29, \"method\":\"tohex_3\", \"params\":[\"tohex\"]}",
  145. "{\"jsonrpc\":\"2.0\", \"id\":29, \"result\":{ \"outParams\":[], \"return\":\"74 6f 68 65 78\"}}"
  146. },
  147. { "{\"jsonrpc\":\"2.0\", \"id\":30, \"method\":\"tohex_3\", \"params\":[\"Größe\"]}", // some non-ASCII BMP chars
  148. "{\"jsonrpc\":\"2.0\", \"id\":30, \"result\":{ \"outParams\":[], \"return\":\"47 72 c3 b6 c3 9f 65\"}}"
  149. },
  150. { "{\"jsonrpc\":\"2.0\", \"id\":30, \"method\":\"tohex_3\", \"params\":[\"\\u0000\\uD83D\\uDE47\"]}", // all hell breaks loose: Non-BMP characters
  151. "{\"jsonrpc\":\"2.0\", \"id\":30, \"result\":{ \"outParams\":[], \"return\":\"00 f0 9f 99 87\"}}"
  152. },
  153. { "{\"jsonrpc\":\"2.0\", \"id\":40, \"method\":\"gen_array\", \"params\":[0]}",
  154. "{\"jsonrpc\":\"2.0\", \"id\":40, \"result\":{ \"outParams\":[], \"return\":[]}}"
  155. },
  156. { "{\"jsonrpc\":\"2.0\", \"id\":40, \"method\":\"gen_array\", \"params\":[1]}",
  157. "{\"jsonrpc\":\"2.0\", \"id\":40, \"result\":{ \"outParams\":[], \"return\":[0]}}"
  158. },
  159. { "{\"jsonrpc\":\"2.0\", \"id\":40, \"method\":\"gen_array\", \"params\":[2]}",
  160. "{\"jsonrpc\":\"2.0\", \"id\":40, \"result\":{ \"outParams\":[], \"return\":[0,1]}}"
  161. },
  162. };
  163. } // end of anonymous namespace
  164. class RpcTest : public ::testing::TestWithParam<TestEntry>
  165. {
  166. // intentionally left blank for now.
  167. };
  168. INSTANTIATE_TEST_CASE_P(RpcTestInstance, RpcTest, testing::ValuesIn(testValues) );
  169. TEST_P( RpcTest, Meh )
  170. {
  171. static DummyAdapter dummyAdapter;
  172. const auto v = GetParam();
  173. js::Value request;
  174. js::read_or_throw(v.input, request);
  175. js::Value expected_result;
  176. js::read_or_throw(v.result, expected_result);
  177. auto r = request;
  178. const js::Value actual_result = call( test_functions, request.get_obj(), &dummyAdapter);
  179. js::Object result_obj = actual_result.get_obj();
  180. js::Object::iterator q = std::find_if(result_obj.begin(), result_obj.end(), [](const js::Pair& v){ return js::Config::get_name(v) == "thread_id"; } );
  181. result_obj.erase( q );
  182. EXPECT_EQ( O{expected_result.get_obj()}, O{result_obj} );
  183. }