1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <limits>
21 #include <locale>
22 #include <ios>
23 #include <iostream>
24 #include <sstream>
25 #include <thrift/protocol/TBinaryProtocol.h>
26 #include <thrift/protocol/TCompactProtocol.h>
27 #include <thrift/protocol/THeaderProtocol.h>
28 #include <thrift/protocol/TJSONProtocol.h>
29 #include <thrift/protocol/TMultiplexedProtocol.h>
30 #include <thrift/transport/THttpClient.h>
31 #include <thrift/transport/TTransportUtils.h>
32 #include <thrift/transport/TSocket.h>
33 #include <thrift/transport/TSSLSocket.h>
34 #include <thrift/transport/TZlibTransport.h>
35 #include <thrift/async/TEvhttpClientChannel.h>
36 #include <thrift/server/TNonblockingServer.h> // <event.h>
37
38 #ifdef HAVE_STDINT_H
39 #include <stdint.h>
40 #endif
41 #ifdef HAVE_INTTYPES_H
42 #include <inttypes.h>
43 #endif
44
45 #include <boost/algorithm/string.hpp>
46 #include <boost/filesystem.hpp>
47 #include <boost/program_options.hpp>
48 #include <boost/random/random_device.hpp>
49 #if _WIN32
50 #include <thrift/windows/TWinsockSingleton.h>
51 #endif
52
53 #include "SecondService.h"
54 #include "ThriftTest.h"
55
56 using namespace std;
57 using namespace apache::thrift;
58 using namespace apache::thrift::async;
59 using namespace apache::thrift::protocol;
60 using namespace apache::thrift::transport;
61 using namespace thrift::test;
62
63 //
64 // A pedantic protocol that checks to make sure the response sequence ID
65 // is the same as the sent sequence ID. lib/cpp always sends zero for
66 // synchronous clients, so this bumps the number to make sure it gets
67 // returned properly from the remote server. Any server that does not
68 // respond with the same sequence number is violating the sequence ID
69 // agreement between client and server.
70 //
71
72 template<typename Proto>
73 class TPedanticProtocol : public Proto
74 {
75 public:
TPedanticProtocol(std::shared_ptr<TTransport> & transport)76 TPedanticProtocol(std::shared_ptr<TTransport>& transport)
77 : Proto(transport), m_last_seqid((std::numeric_limits<int32_t>::max)() - 10) { }
78
writeMessageBegin_virt(const std::string & name,const TMessageType messageType,const int32_t in_seqid)79 virtual uint32_t writeMessageBegin_virt(const std::string& name,
80 const TMessageType messageType,
81 const int32_t in_seqid) override
82 {
83 int32_t seqid = in_seqid;
84 if (!seqid) { // this is typical for normal cpp generated code
85 seqid = ++m_last_seqid;
86 }
87
88 return Proto::writeMessageBegin_virt(name, messageType, seqid);
89 }
90
readMessageBegin_virt(std::string & name,TMessageType & messageType,int32_t & seqid)91 virtual uint32_t readMessageBegin_virt(std::string& name,
92 TMessageType& messageType,
93 int32_t& seqid) override
94 {
95 uint32_t result = Proto::readMessageBegin_virt(name, messageType, seqid);
96 if (seqid != m_last_seqid) {
97 std::stringstream ss;
98 ss << "ERROR: send request with seqid " << m_last_seqid << " and got reply with seqid " << seqid;
99 throw std::logic_error(ss.str());
100 } /* else {
101 std::cout << "verified seqid " << m_last_seqid << " round trip OK" << std::endl;
102 } */
103 return result;
104 }
105
106 private:
107 int32_t m_last_seqid;
108 };
109
110 // Current time, microseconds since the epoch
now()111 uint64_t now() {
112 int64_t ret;
113 struct timeval tv;
114
115 THRIFT_GETTIMEOFDAY(&tv, nullptr);
116 ret = tv.tv_sec;
117 ret = ret * 1000 * 1000 + tv.tv_usec;
118 return ret;
119 }
120
testString_clientReturn(event_base * base,int testNr,ThriftTestCobClient * client)121 static void testString_clientReturn(event_base* base,
122 int testNr,
123 ThriftTestCobClient* client) {
124 try {
125 string s;
126 client->recv_testString(s);
127 std::ostringstream os;
128 os << "test" << testNr;
129 const bool ok = (s == os.str());
130 cout << "testString: " << s << " " << ((ok) ? "ok" : "failed") << endl;
131 } catch (TException& exn) {
132 cout << "Error: " << exn.what() << endl;
133 }
134
135 if (testNr == 9)
136 event_base_loopbreak(base); // end test
137 }
138
testVoid_clientReturn(event_base * base,ThriftTestCobClient * client)139 static void testVoid_clientReturn(event_base* base, ThriftTestCobClient* client) {
140 try {
141 client->recv_testVoid();
142 cout << "testVoid" << endl;
143
144 for (int testNr = 0; testNr < 10; ++testNr) {
145 std::ostringstream os;
146 os << "test" << testNr;
147 client->testString(std::bind(testString_clientReturn,
148 base,
149 testNr,
150 std::placeholders::_1),
151 os.str());
152 }
153 } catch (TException& exn) {
154 cout << "Error: " << exn.what() << endl;
155 }
156 }
157
158 // Workaround for absense of C++11 "auto" keyword.
159 template <typename T>
print_eq(T expected,T actual)160 bool print_eq(T expected, T actual) {
161 cout << "(" << actual << ")" << endl;
162 if (expected != actual) {
163 cout << "*** FAILED ***" << endl << "Expected: " << expected << " but got: " << actual << endl;
164 return false;
165 }
166 return true;
167 }
168
169 #define BASETYPE_IDENTITY_TEST(func, value) \
170 cout << #func "(" << value << ") = "; \
171 try { \
172 if (!print_eq(value, testClient.func(value))) \
173 return_code |= ERR_BASETYPES; \
174 } catch (TTransportException&) { \
175 throw; \
176 } catch (exception & ex) { \
177 cout << "*** FAILED ***" << endl << ex.what() << endl; \
178 return_code |= ERR_BASETYPES; \
179 }
180
181 int binary_test(ThriftTestClient& testClient, string::size_type siz);
182
183 BOOST_CONSTEXPR_OR_CONST int ERR_BASETYPES = 1;
184 BOOST_CONSTEXPR_OR_CONST int ERR_STRUCTS = 2;
185 BOOST_CONSTEXPR_OR_CONST int ERR_CONTAINERS = 4;
186 BOOST_CONSTEXPR_OR_CONST int ERR_EXCEPTIONS = 8;
187 BOOST_CONSTEXPR_OR_CONST int ERR_UNKNOWN = 64;
188
main(int argc,char ** argv)189 int main(int argc, char** argv) {
190 cout.precision(19);
191
192 string testDir = boost::filesystem::system_complete(argv[0]).parent_path().parent_path().parent_path().string();
193 string caPath = testDir + "/keys/CA.pem";
194 string certPath = testDir + "/keys/client.crt";
195 string keyPath = testDir + "/keys/client.key";
196
197 #if _WIN32
198 transport::TWinsockSingleton::create();
199 #endif
200 string host = "localhost";
201 int port = 9090;
202 int numTests = 1;
203 bool ssl = false;
204 bool zlib = false;
205 string transport_type = "buffered";
206 string protocol_type = "binary";
207 string domain_socket = "";
208 bool abstract_namespace = false;
209 bool noinsane = false;
210
211 int return_code = 0;
212
213 boost::program_options::options_description desc("Allowed options");
214 desc.add_options()
215 ("help,h", "produce help message")
216 ("host",
217 boost::program_options::value<string>(&host)->default_value(host),
218 "Host to connect")
219 ("port",
220 boost::program_options::value<int>(&port)->default_value(port),
221 "Port number to connect")
222 ("domain-socket",
223 boost::program_options::value<string>(&domain_socket)->default_value(domain_socket),
224 "Domain Socket (e.g. /tmp/ThriftTest.thrift), instead of host and port")
225 ("abstract-namespace",
226 "Look for the domain socket in the Abstract Namespace"
227 " (no connection with filesystem pathnames)")
228 ("transport",
229 boost::program_options::value<string>(&transport_type)->default_value(transport_type),
230 "Transport: buffered, framed, http, evhttp, zlib")
231 ("protocol",
232 boost::program_options::value<string>(&protocol_type)->default_value(protocol_type),
233 "Protocol: binary, compact, header, json, multi, multic, multih, multij")
234 ("ssl",
235 "Encrypted Transport using SSL")
236 ("zlib",
237 "Wrap Transport with Zlib")
238 ("testloops,n",
239 boost::program_options::value<int>(&numTests)->default_value(numTests),
240 "Number of Tests")
241 ("noinsane",
242 "Do not run insanity test");
243
244 boost::program_options::variables_map vm;
245 boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
246 boost::program_options::notify(vm);
247
248 if (vm.count("help")) {
249 cout << desc << endl;
250 return ERR_UNKNOWN;
251 }
252
253 try {
254 if (!protocol_type.empty()) {
255 if (protocol_type == "binary") {
256 } else if (protocol_type == "compact") {
257 } else if (protocol_type == "header") {
258 } else if (protocol_type == "json") {
259 } else if (protocol_type == "multi") {
260 } else if (protocol_type == "multic") {
261 } else if (protocol_type == "multih") {
262 } else if (protocol_type == "multij") {
263 } else {
264 throw invalid_argument("Unknown protocol type " + protocol_type);
265 }
266 }
267
268 if (!transport_type.empty()) {
269 if (transport_type == "buffered") {
270 } else if (transport_type == "framed") {
271 } else if (transport_type == "http") {
272 } else if (transport_type == "evhttp") {
273 } else if (transport_type == "zlib") {
274 // crosstest will pass zlib as a transport and as a flag right now..
275 } else {
276 throw invalid_argument("Unknown transport type " + transport_type);
277 }
278 }
279
280 } catch (exception& e) {
281 cerr << e.what() << endl;
282 cout << desc << endl;
283 return ERR_UNKNOWN;
284 }
285
286 if (vm.count("ssl")) {
287 ssl = true;
288 }
289
290 if (vm.count("zlib")) {
291 zlib = true;
292 }
293
294 if (vm.count("abstract-namespace")) {
295 abstract_namespace = true;
296 }
297
298 if (vm.count("noinsane")) {
299 noinsane = true;
300 }
301
302 // THRIFT-4164: The factory MUST outlive any sockets it creates for correct behavior!
303 std::shared_ptr<TSSLSocketFactory> factory;
304 std::shared_ptr<TSocket> socket;
305 std::shared_ptr<TTransport> transport;
306 std::shared_ptr<TProtocol> protocol;
307 std::shared_ptr<TProtocol> protocol2; // SecondService for multiplexed
308
309 if (ssl) {
310 cout << "Client Certificate File: " << certPath << endl;
311 cout << "Client Key File: " << keyPath << endl;
312 cout << "CA File: " << caPath << endl;
313
314 factory = std::shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory());
315 factory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
316 factory->loadTrustedCertificates(caPath.c_str());
317 factory->loadCertificate(certPath.c_str());
318 factory->loadPrivateKey(keyPath.c_str());
319 factory->authenticate(true);
320 socket = factory->createSocket(host, port);
321 } else {
322 if (domain_socket != "") {
323 if (abstract_namespace) {
324 std::string abstract_socket("\0", 1);
325 abstract_socket += domain_socket;
326 socket = std::shared_ptr<TSocket>(new TSocket(abstract_socket));
327 } else {
328 socket = std::shared_ptr<TSocket>(new TSocket(domain_socket));
329 }
330 port = 0;
331 } else {
332 socket = std::shared_ptr<TSocket>(new TSocket(host, port));
333 }
334 }
335
336 if (transport_type.compare("http") == 0) {
337 transport = std::make_shared<THttpClient>(socket, host, "/service");
338 } else if (transport_type.compare("framed") == 0) {
339 transport = std::make_shared<TFramedTransport>(socket);
340 } else {
341 transport = std::make_shared<TBufferedTransport>(socket);
342 }
343
344 if (zlib) {
345 transport = std::make_shared<TZlibTransport>(transport);
346 }
347
348 if (protocol_type == "json" || protocol_type == "multij") {
349 typedef TPedanticProtocol<TJSONProtocol> TPedanticJSONProtocol;
350 protocol = std::make_shared<TPedanticJSONProtocol>(transport);
351 } else if (protocol_type == "compact" || protocol_type == "multic") {
352 typedef TPedanticProtocol<TCompactProtocol> TPedanticCompactProtocol;
353 protocol = std::make_shared<TPedanticCompactProtocol>(transport);
354 } else if (protocol_type == "header" || protocol_type == "multih") {
355 typedef TPedanticProtocol<THeaderProtocol> TPedanticHeaderProtocol;
356 protocol = std::make_shared<TPedanticHeaderProtocol>(transport);
357 } else {
358 typedef TPedanticProtocol<TBinaryProtocol> TPedanticBinaryProtocol;
359 protocol = std::make_shared<TPedanticBinaryProtocol>(transport);
360 }
361
362 if (boost::starts_with(protocol_type, "multi")) {
363 protocol2 = std::make_shared<TMultiplexedProtocol>(protocol, "SecondService");
364 // we don't need access to the original protocol any more, so...
365 protocol = std::make_shared<TMultiplexedProtocol>(protocol, "ThriftTest");
366 }
367
368 // Connection info
369 cout << "Connecting (" << transport_type << "/" << protocol_type << ") to: ";
370 if (abstract_namespace) {
371 cout << '@';
372 }
373 cout << domain_socket;
374 if (port != 0) {
375 cout << host << ":" << port;
376 }
377 cout << endl;
378
379 if (transport_type.compare("evhttp") == 0) {
380 event_base* base = event_base_new();
381 cout << "Libevent Version: " << event_get_version() << endl;
382 cout << "Libevent Method: " << event_base_get_method(base) << endl;
383 #if LIBEVENT_VERSION_NUMBER >= 0x02000000
384 cout << "Libevent Features: 0x" << hex << event_base_get_features(base) << endl;
385 #endif
386
387 std::shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
388
389 std::shared_ptr<TAsyncChannel> channel(
390 new TEvhttpClientChannel(host.c_str(), "/", host.c_str(), port, base));
391 ThriftTestCobClient* client = new ThriftTestCobClient(channel, protocolFactory.get());
392 client->testVoid(std::bind(testVoid_clientReturn,
393 base,
394 std::placeholders::_1));
395
396 event_base_loop(base, 0);
397 return 0;
398 }
399
400 ThriftTestClient testClient(protocol);
401
402 uint64_t time_min = 0;
403 uint64_t time_max = 0;
404 uint64_t time_tot = 0;
405
406 int test = 0;
407 for (test = 0; test < numTests; ++test) {
408
409 try {
410 transport->open();
411 } catch (TTransportException& ex) {
412 cout << "Connect failed: " << ex.what() << endl;
413 return ERR_UNKNOWN;
414 }
415
416 /**
417 * CONNECT TEST
418 */
419 printf("Test #%d, connect %s:%d\n", test + 1, host.c_str(), port);
420
421 uint64_t start = now();
422
423 /**
424 * VOID TEST
425 */
426 try {
427 cout << "testVoid()" << flush;
428 testClient.testVoid();
429 cout << " = void" << endl;
430 } catch (TTransportException&) {
431 // Stop here if transport got broken
432 throw;
433 } catch (exception& ex) {
434 cout << "*** FAILED ***" << endl << ex.what() << endl;
435 return_code |= ERR_BASETYPES;
436 }
437
438 /**
439 * STRING TEST
440 */
441 cout << "testString(\"Test\")" << flush;
442 string s;
443 testClient.testString(s, "Test");
444 cout << " = " << s << endl;
445 if (s != "Test") {
446 cout << "*** FAILED ***" << endl;
447 return_code |= ERR_BASETYPES;
448 }
449
450 //
451 // Multiplexed protocol - call another service method
452 // in the middle of the ThriftTest
453 //
454 if (boost::starts_with(protocol_type, "multi")) {
455 SecondServiceClient ssc(protocol2);
456 // transport is already open...
457
458 try {
459 cout << "secondService.secondTestString(\"foo\") => " << flush;
460 std::string result;
461 ssc.secondtestString(result, "foo");
462 cout << "{" << result << "}" << endl;
463 } catch (std::exception& e) {
464 cout << " *** FAILED *** " << e.what() << endl;
465 return_code |= ERR_EXCEPTIONS;
466 }
467 }
468
469 try {
470 #ifdef _MSC_VER
471 #pragma warning( push )
472 #pragma warning( disable : 4566 )
473 #endif
474 string str(
475 "}{Afrikaans, Alemannisch, Aragonés, العربية, مصرى, "
476 "Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, "
477 "Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, "
478 "বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, "
479 "Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, "
480 "Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, "
481 "Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, "
482 "Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, "
483 "Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, "
484 "Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, "
485 "Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, "
486 "ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, "
487 "Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, "
488 "Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa "
489 "Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, مازِرونی, Bahasa "
490 "Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, "
491 "Norsk (nynorsk), Norsk (bokmål), Nouormand, Diné bizaad, "
492 "Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو, "
493 "Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română, "
494 "Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple "
495 "English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, "
496 "Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, "
497 "Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, "
498 "Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, "
499 "Bân-lâm-gú, 粵語");
500 #ifdef _MSC_VER
501 #pragma warning( pop )
502 #endif
503 cout << "testString(" << str << ") = " << flush;
504 testClient.testString(s, str);
505 cout << s << endl;
506 if (s != str) {
507 cout.imbue(locale("en_US.UTF8"));
508 cout << "*** FAILED ***" << endl << "Expected string: " << str << " but got: " << s << endl << "CLEAR";
509 return_code |= ERR_BASETYPES;
510 }
511 } catch (TTransportException&) {
512 throw;
513 } catch (exception& ex) {
514 cout << "*** FAILED ***" << endl << ex.what() << endl;
515 return_code |= ERR_BASETYPES;
516 return return_code;
517 }
518 try {
519 string str(
520 "quote: \" backslash:"
521 " forwardslash-escaped: \\/ "
522 " backspace: \b formfeed: \f newline: \n return: \r tab: "
523 " now-all-of-them-together: \"\\\\/\b\n\r\t"
524 " now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><"
525 " char-to-test-json-parsing: ]] \"]] \\\" }}}{ [[[ ");
526 cout << "testString(" << str << ") = " << flush;
527 testClient.testString(s, str);
528 cout << s << endl;
529 if (s != str) {
530 cout.imbue(locale("en_US.UTF8"));
531 cout << "*** FAILED ***" << endl
532 << "Expected string: " << str << " but got: " << s << endl
533 << "CLEAR";
534 ;
535 return_code |= ERR_BASETYPES;
536 }
537 } catch (TTransportException&) {
538 throw;
539 } catch (exception& ex) {
540 cout << "*** FAILED ***" << endl << ex.what() << endl;
541 return_code |= ERR_BASETYPES;
542 return return_code;
543 }
544
545 /**
546 * BOOL TEST
547 */
548 cout << boolalpha;
549 BASETYPE_IDENTITY_TEST(testBool, true);
550 BASETYPE_IDENTITY_TEST(testBool, false);
551
552 /**
553 * BYTE TEST
554 */
555 BASETYPE_IDENTITY_TEST(testByte, (int8_t)0);
556 BASETYPE_IDENTITY_TEST(testByte, (int8_t)-1);
557 BASETYPE_IDENTITY_TEST(testByte, (int8_t)42);
558 BASETYPE_IDENTITY_TEST(testByte, (int8_t)-42);
559 BASETYPE_IDENTITY_TEST(testByte, (int8_t)127);
560 BASETYPE_IDENTITY_TEST(testByte, (int8_t)-128);
561
562 /**
563 * I32 TEST
564 */
565 BASETYPE_IDENTITY_TEST(testI32, 0);
566 BASETYPE_IDENTITY_TEST(testI32, -1);
567 BASETYPE_IDENTITY_TEST(testI32, 190000013);
568 BASETYPE_IDENTITY_TEST(testI32, -190000013);
569 BASETYPE_IDENTITY_TEST(testI32, (numeric_limits<int32_t>::max)());
570 BASETYPE_IDENTITY_TEST(testI32, (numeric_limits<int32_t>::min)());
571
572 /**
573 * I64 TEST
574 */
575 BASETYPE_IDENTITY_TEST(testI64, (int64_t)0);
576 BASETYPE_IDENTITY_TEST(testI64, (int64_t)-1);
577 BASETYPE_IDENTITY_TEST(testI64, (int64_t)7000000000000000123LL);
578 BASETYPE_IDENTITY_TEST(testI64, (int64_t)-7000000000000000123LL);
579 BASETYPE_IDENTITY_TEST(testI64, (int64_t)pow(static_cast<double>(2LL), 32));
580 BASETYPE_IDENTITY_TEST(testI64, (int64_t)-pow(static_cast<double>(2LL), 32));
581 BASETYPE_IDENTITY_TEST(testI64, (int64_t)pow(static_cast<double>(2LL), 32) + 1);
582 BASETYPE_IDENTITY_TEST(testI64, (int64_t)-pow(static_cast<double>(2LL), 32) - 1);
583 BASETYPE_IDENTITY_TEST(testI64, (numeric_limits<int64_t>::max)());
584 BASETYPE_IDENTITY_TEST(testI64, (numeric_limits<int64_t>::min)());
585
586 /**
587 * DOUBLE TEST
588 */
589 // Comparing double values with plain equality because Thrift handles full precision of double
590 BASETYPE_IDENTITY_TEST(testDouble, 0.0);
591 BASETYPE_IDENTITY_TEST(testDouble, -1.0);
592 BASETYPE_IDENTITY_TEST(testDouble, -5.2098523);
593 BASETYPE_IDENTITY_TEST(testDouble, -0.000341012439638598279);
594 BASETYPE_IDENTITY_TEST(testDouble, pow(static_cast<double>(2), 32));
595 BASETYPE_IDENTITY_TEST(testDouble, pow(static_cast<double>(2), 32) + 1);
596 BASETYPE_IDENTITY_TEST(testDouble, pow(static_cast<double>(2), 53) - 1);
597 BASETYPE_IDENTITY_TEST(testDouble, -pow(static_cast<double>(2), 32));
598 BASETYPE_IDENTITY_TEST(testDouble, -pow(static_cast<double>(2), 32) - 1);
599 BASETYPE_IDENTITY_TEST(testDouble, -pow(static_cast<double>(2), 53) + 1);
600
601 try {
602 double expected = pow(static_cast<double>(10), 307);
603 cout << "testDouble(" << expected << ") = " << flush;
604 double actual = testClient.testDouble(expected);
605 cout << "(" << actual << ")" << endl;
606 if (expected - actual > pow(static_cast<double>(10), 292)) {
607 cout << "*** FAILED ***" << endl
608 << "Expected: " << expected << " but got: " << actual << endl;
609 }
610 } catch (TTransportException&) {
611 throw;
612 } catch (exception& ex) {
613 cout << "*** FAILED ***" << endl << ex.what() << endl;
614 return_code |= ERR_BASETYPES;
615 }
616
617 try {
618 double expected = pow(static_cast<double>(10), -292);
619 cout << "testDouble(" << expected << ") = " << flush;
620 double actual = testClient.testDouble(expected);
621 cout << "(" << actual << ")" << endl;
622 if (expected - actual > pow(static_cast<double>(10), -307)) {
623 cout << "*** FAILED ***" << endl
624 << "Expected: " << expected << " but got: " << actual << endl;
625 }
626 } catch (TTransportException&) {
627 throw;
628 } catch (exception& ex) {
629 cout << "*** FAILED ***" << endl << ex.what() << endl;
630 return_code |= ERR_BASETYPES;
631 }
632
633 /**
634 * BINARY TEST
635 */
636 for (string::size_type i = 0; i < 131073 && !return_code; ) {
637 return_code |= binary_test(testClient, i);
638 if (i > 0) { i *= 2; } else { ++i; }
639 }
640
641
642 /**
643 * STRUCT TEST
644 */
645 cout << "testStruct({\"Zero\", 1, -3, -5})" << flush;
646 Xtruct out;
647 out.string_thing = "Zero";
648 out.byte_thing = 1;
649 out.i32_thing = -3;
650 out.i64_thing = -5;
651 Xtruct in;
652 testClient.testStruct(in, out);
653 printf(" = {\"%s\", %d, %d, %" PRId64 "}\n",
654 in.string_thing.c_str(),
655 (int)in.byte_thing,
656 in.i32_thing,
657 in.i64_thing);
658 if (in != out) {
659 cout << "*** FAILED ***" << endl;
660 return_code |= ERR_STRUCTS;
661 }
662
663 /**
664 * NESTED STRUCT TEST
665 */
666 cout << "testNest({1, {\"Zero\", 1, -3, -5}), 5}" << flush;
667 Xtruct2 out2;
668 out2.byte_thing = 1;
669 out2.struct_thing = out;
670 out2.i32_thing = 5;
671 Xtruct2 in2;
672 testClient.testNest(in2, out2);
673 in = in2.struct_thing;
674 printf(" = {%d, {\"%s\", %d, %d, %" PRId64 "}, %d}\n",
675 in2.byte_thing,
676 in.string_thing.c_str(),
677 (int)in.byte_thing,
678 in.i32_thing,
679 in.i64_thing,
680 in2.i32_thing);
681 if (in2 != out2) {
682 cout << "*** FAILED ***" << endl;
683 return_code |= ERR_STRUCTS;
684 }
685
686 /**
687 * MAP TEST
688 */
689 map<int32_t, int32_t> mapout;
690 for (int32_t i = 0; i < 5; ++i) {
691 mapout.insert(make_pair(i, i - 10));
692 }
693 cout << "testMap({" << flush;
694 map<int32_t, int32_t>::const_iterator m_iter;
695 bool first = true;
696 for (m_iter = mapout.begin(); m_iter != mapout.end(); ++m_iter) {
697 if (first) {
698 first = false;
699 } else {
700 cout << ",";
701 }
702 cout << m_iter->first << " => " << m_iter->second;
703 }
704 cout << "})";
705 map<int32_t, int32_t> mapin;
706 testClient.testMap(mapin, mapout);
707 cout << " = {";
708 first = true;
709 for (m_iter = mapin.begin(); m_iter != mapin.end(); ++m_iter) {
710 if (first) {
711 first = false;
712 } else {
713 cout << ",";
714 }
715 cout << m_iter->first << " => " << m_iter->second;
716 }
717 cout << "}" << endl;
718 if (mapin != mapout) {
719 cout << "*** FAILED ***" << endl;
720 return_code |= ERR_CONTAINERS;
721 }
722
723 /**
724 * STRING MAP TEST
725 */
726 cout << "testStringMap({a => 2, b => blah, some => thing}) = {" << flush;
727 map<string, string> smapin;
728 map<string, string> smapout;
729 smapin["a"] = "2";
730 smapin["b"] = "blah";
731 smapin["some"] = "thing";
732 try {
733 testClient.testStringMap(smapout, smapin);
734 first = true;
735 for (map<string, string>::const_iterator it = smapout.begin(); it != smapout.end(); ++it) {
736 if (first)
737 cout << ",";
738 else
739 first = false;
740 cout << it->first << " => " << it->second;
741 }
742 cout << "}" << endl;
743 if (smapin != smapout) {
744 cout << "*** FAILED ***" << endl;
745 return_code |= ERR_CONTAINERS;
746 }
747 } catch (TTransportException&) {
748 throw;
749 } catch (exception& ex) {
750 cout << "*** FAILED ***" << endl << ex.what() << endl;
751 return_code |= ERR_CONTAINERS;
752 }
753
754 /**
755 * SET TEST
756 */
757 set<int32_t> setout;
758 for (int32_t i = -2; i < 3; ++i) {
759 setout.insert(i);
760 }
761 cout << "testSet({" << flush;
762 set<int32_t>::const_iterator s_iter;
763 first = true;
764 for (s_iter = setout.begin(); s_iter != setout.end(); ++s_iter) {
765 if (first) {
766 first = false;
767 } else {
768 cout << ",";
769 }
770 cout << *s_iter;
771 }
772 cout << "})";
773 set<int32_t> setin;
774 testClient.testSet(setin, setout);
775 cout << " = {";
776 first = true;
777 for (s_iter = setin.begin(); s_iter != setin.end(); ++s_iter) {
778 if (first) {
779 first = false;
780 } else {
781 cout << ",";
782 }
783 cout << *s_iter;
784 }
785 cout << "}" << endl;
786 if (setin != setout) {
787 cout << "*** FAILED ***" << endl;
788 return_code |= ERR_CONTAINERS;
789 }
790
791 /**
792 * LIST TEST
793 */
794 cout << "testList(empty)" << flush;
795 try {
796 vector<int32_t> listout;
797 testClient.testList(listout, vector<int32_t>());
798 if (!listout.empty()) {
799 cout << "*** FAILED ***" << endl;
800 cout << "invalid length: " << listout.size() << endl;
801 return_code |= ERR_CONTAINERS;
802 }
803 } catch (TTransportException&) {
804 throw;
805 } catch (exception& ex) {
806 cout << "*** FAILED ***" << endl << ex.what() << endl;
807 return_code |= ERR_CONTAINERS;
808 }
809 try {
810 vector<int32_t> listout;
811 for (int32_t i = -2; i < 3; ++i) {
812 listout.push_back(i);
813 }
814 cout << "testList({" << flush;
815 vector<int32_t>::const_iterator l_iter;
816 first = true;
817 for (l_iter = listout.begin(); l_iter != listout.end(); ++l_iter) {
818 if (first) {
819 first = false;
820 } else {
821 cout << ",";
822 }
823 cout << *l_iter;
824 }
825 cout << "})";
826 vector<int32_t> listin;
827 testClient.testList(listin, listout);
828 cout << " = {";
829 first = true;
830 for (l_iter = listin.begin(); l_iter != listin.end(); ++l_iter) {
831 if (first) {
832 first = false;
833 } else {
834 cout << ",";
835 }
836 cout << *l_iter;
837 }
838 cout << "}" << endl;
839 if (listin != listout) {
840 cout << "*** FAILED ***" << endl;
841 return_code |= ERR_CONTAINERS;
842 }
843 } catch (TTransportException&) {
844 throw;
845 } catch (exception& ex) {
846 cout << "*** FAILED ***" << endl << ex.what() << endl;
847 return_code |= ERR_CONTAINERS;
848 }
849
850 /**
851 * ENUM TEST
852 */
853 cout << "testEnum(ONE)" << flush;
854 Numberz::type ret = testClient.testEnum(Numberz::ONE);
855 cout << " = " << ret << endl;
856 if (ret != Numberz::ONE) {
857 cout << "*** FAILED ***" << endl;
858 return_code |= ERR_STRUCTS;
859 }
860
861 cout << "testEnum(TWO)" << flush;
862 ret = testClient.testEnum(Numberz::TWO);
863 cout << " = " << ret << endl;
864 if (ret != Numberz::TWO) {
865 cout << "*** FAILED ***" << endl;
866 return_code |= ERR_STRUCTS;
867 }
868
869 cout << "testEnum(THREE)" << flush;
870 ret = testClient.testEnum(Numberz::THREE);
871 cout << " = " << ret << endl;
872 if (ret != Numberz::THREE) {
873 cout << "*** FAILED ***" << endl;
874 return_code |= ERR_STRUCTS;
875 }
876
877 cout << "testEnum(FIVE)" << flush;
878 ret = testClient.testEnum(Numberz::FIVE);
879 cout << " = " << ret << endl;
880 if (ret != Numberz::FIVE) {
881 cout << "*** FAILED ***" << endl;
882 return_code |= ERR_STRUCTS;
883 }
884
885 cout << "testEnum(EIGHT)" << flush;
886 ret = testClient.testEnum(Numberz::EIGHT);
887 cout << " = " << ret << endl;
888 if (ret != Numberz::EIGHT) {
889 cout << "*** FAILED ***" << endl;
890 return_code |= ERR_STRUCTS;
891 }
892
893 /**
894 * TYPEDEF TEST
895 */
896 cout << "testTypedef(309858235082523)" << flush;
897 UserId uid = testClient.testTypedef(309858235082523LL);
898 cout << " = " << uid << endl;
899 if (uid != 309858235082523LL) {
900 cout << "*** FAILED ***" << endl;
901 return_code |= ERR_STRUCTS;
902 }
903
904 /**
905 * NESTED MAP TEST
906 */
907 cout << "testMapMap(1)" << flush;
908 map<int32_t, map<int32_t, int32_t> > mm;
909 testClient.testMapMap(mm, 1);
910 cout << " = {";
911 map<int32_t, map<int32_t, int32_t> >::const_iterator mi;
912 for (mi = mm.begin(); mi != mm.end(); ++mi) {
913 printf("%d => {", mi->first);
914 map<int32_t, int32_t>::const_iterator mi2;
915 for (mi2 = mi->second.begin(); mi2 != mi->second.end(); ++mi2) {
916 cout << mi2->first << " => " << mi2->second;
917 }
918 cout << "}, ";
919 }
920 cout << "}" << endl;
921 if (mm.size() != 2 ||
922 mm[-4][-4] != -4 ||
923 mm[-4][-3] != -3 ||
924 mm[-4][-2] != -2 ||
925 mm[-4][-1] != -1 ||
926 mm[4][4] != 4 ||
927 mm[4][3] != 3 ||
928 mm[4][2] != 2 ||
929 mm[4][1] != 1) {
930 cout << "*** FAILED ***" << endl;
931 return_code |= ERR_CONTAINERS;
932 }
933
934 /**
935 * INSANITY TEST
936 */
937 if (!noinsane) {
938 Insanity insane;
939 insane.userMap.insert(make_pair(Numberz::FIVE, 5));
940 insane.userMap.insert(make_pair(Numberz::EIGHT, 8));
941 Xtruct truck;
942 truck.string_thing = "Goodbye4";
943 truck.byte_thing = 4;
944 truck.i32_thing = 4;
945 truck.i64_thing = 4;
946 Xtruct truck2;
947 truck2.string_thing = "Hello2";
948 truck2.byte_thing = 2;
949 truck2.i32_thing = 2;
950 truck2.i64_thing = 2;
951 insane.xtructs.push_back(truck);
952 insane.xtructs.push_back(truck2);
953 cout << "testInsanity()" << flush;
954 map<UserId, map<Numberz::type, Insanity> > whoa;
955 testClient.testInsanity(whoa, insane);
956 cout << " = {";
957 map<UserId, map<Numberz::type, Insanity> >::const_iterator i_iter;
958 for (i_iter = whoa.begin(); i_iter != whoa.end(); ++i_iter) {
959 printf("%" PRId64 " => {", i_iter->first);
960 map<Numberz::type, Insanity>::const_iterator i2_iter;
961 for (i2_iter = i_iter->second.begin(); i2_iter != i_iter->second.end(); ++i2_iter) {
962 printf("%d => {", i2_iter->first);
963 map<Numberz::type, UserId> userMap = i2_iter->second.userMap;
964 map<Numberz::type, UserId>::const_iterator um;
965 cout << "{";
966 for (um = userMap.begin(); um != userMap.end(); ++um) {
967 cout << um->first << " => " << um->second;
968 }
969 cout << "}, ";
970
971 vector<Xtruct> xtructs = i2_iter->second.xtructs;
972 vector<Xtruct>::const_iterator x;
973 cout << "{";
974 for (x = xtructs.begin(); x != xtructs.end(); ++x) {
975 printf("{\"%s\", %d, %d, %" PRId64 "}, ",
976 x->string_thing.c_str(),
977 (int)x->byte_thing,
978 x->i32_thing,
979 x->i64_thing);
980 }
981 cout << "}";
982
983 cout << "}, ";
984 }
985 cout << "}, ";
986 }
987 cout << "}" << endl;
988 bool failed = false;
989 map<UserId, map<Numberz::type, Insanity> >::const_iterator it1 = whoa.find(UserId(1));
990 if (whoa.size() != 2) {
991 failed = true;
992 }
993 if (it1 == whoa.end()) {
994 failed = true;
995 } else {
996 auto it12 = it1->second.find(Numberz::TWO);
997 if (it12 == it1->second.end() || it12->second != insane) {
998 failed = true;
999 }
1000 auto it13 = it1->second.find(Numberz::THREE);
1001 if (it13 == it1->second.end() || it13->second != insane) {
1002 failed = true;
1003 }
1004 }
1005 map<UserId, map<Numberz::type, Insanity> >::const_iterator it2 = whoa.find(UserId(2));
1006 if (it2 == whoa.end()) {
1007 failed = true;
1008 } else {
1009 auto it26 = it2->second.find(Numberz::SIX);
1010 if (it26 == it2->second.end() || it26->second != Insanity()) {
1011 failed = true;
1012 }
1013 }
1014 if (failed) {
1015 cout << "*** FAILED ***" << endl;
1016 return_code |= ERR_STRUCTS;
1017 }
1018 }
1019
1020 /**
1021 * MULTI TEST
1022 */
1023 cout << "testMulti()" << endl;
1024 try {
1025 map<int16_t, string> mul_map;
1026 Xtruct mul_result;
1027 mul_map[1] = "blah";
1028 mul_map[2] = "thing";
1029 testClient.testMulti(mul_result, 42, 4242, 424242, mul_map, Numberz::EIGHT, UserId(24));
1030 Xtruct xxs;
1031 xxs.string_thing = "Hello2";
1032 xxs.byte_thing = 42;
1033 xxs.i32_thing = 4242;
1034 xxs.i64_thing = 424242;
1035 if (mul_result != xxs) {
1036 cout << "*** FAILED ***" << endl;
1037 return_code |= ERR_STRUCTS;
1038 }
1039 } catch (TTransportException&) {
1040 throw;
1041 } catch (exception& ex) {
1042 cout << "*** FAILED ***" << endl << ex.what() << endl;
1043 return_code |= ERR_STRUCTS;
1044 }
1045
1046 /* test exception */
1047
1048 try {
1049 cout << "testClient.testException(\"Xception\") =>" << flush;
1050 testClient.testException("Xception");
1051 cout << " void\n*** FAILED ***" << endl;
1052 return_code |= ERR_EXCEPTIONS;
1053
1054 } catch (Xception& e) {
1055 printf(" {%u, \"%s\"}\n", e.errorCode, e.message.c_str());
1056 }
1057
1058 try {
1059 cout << "testClient.testException(\"TException\") =>" << flush;
1060 testClient.testException("TException");
1061 cout << " void\n*** FAILED ***" << endl;
1062 return_code |= ERR_EXCEPTIONS;
1063
1064 } catch (const TException&) {
1065 cout << " Caught TException" << endl;
1066 }
1067
1068 try {
1069 cout << "testClient.testException(\"success\") =>" << flush;
1070 testClient.testException("success");
1071 cout << " void" << endl;
1072 } catch (exception & ex) { \
1073 cout << "*** FAILED ***" << endl << ex.what() << endl;
1074 return_code |= ERR_EXCEPTIONS;
1075 }
1076
1077 /* test multi exception */
1078
1079 try {
1080 cout << "testClient.testMultiException(\"Xception\", \"test 1\") =>" << flush;
1081 Xtruct result;
1082 testClient.testMultiException(result, "Xception", "test 1");
1083 cout << " result\n*** FAILED ***" << endl;
1084 return_code |= ERR_EXCEPTIONS;
1085 } catch (Xception& e) {
1086 printf(" {%u, \"%s\"}\n", e.errorCode, e.message.c_str());
1087 }
1088
1089 try {
1090 cout << "testClient.testMultiException(\"Xception2\", \"test 2\") =>" << flush;
1091 Xtruct result;
1092 testClient.testMultiException(result, "Xception2", "test 2");
1093 cout << " result\n*** FAILED ***" << endl;
1094 return_code |= ERR_EXCEPTIONS;
1095
1096 } catch (Xception2& e) {
1097 printf(" {%u, {\"%s\"}}\n", e.errorCode, e.struct_thing.string_thing.c_str());
1098 }
1099
1100 try {
1101 cout << "testClient.testMultiException(\"success\", \"test 3\") =>" << flush;
1102 Xtruct result;
1103 testClient.testMultiException(result, "success", "test 3");
1104 printf(" {{\"%s\"}}\n", result.string_thing.c_str());
1105 } catch (exception & ex) { \
1106 cout << "*** FAILED ***" << endl << ex.what() << endl;
1107 return_code |= ERR_EXCEPTIONS;
1108 }
1109
1110 /* test oneway void */
1111 {
1112 cout << "testClient.testOneway(1) =>" << flush;
1113 uint64_t startOneway = now();
1114 testClient.testOneway(1);
1115 uint64_t elapsed = now() - startOneway;
1116 if (elapsed > 200 * 1000) { // 0.2 seconds
1117 printf("*** FAILED *** - took %.2f ms\n", (double)elapsed / 1000.0);
1118 return_code |= ERR_BASETYPES;
1119 } else {
1120 printf(" success - took %.2f ms\n", (double)elapsed / 1000.0);
1121 }
1122 }
1123
1124 /**
1125 * redo a simple test after the oneway to make sure we aren't "off by one" --
1126 * if the server treated oneway void like normal void, this next test will
1127 * fail since it will get the void confirmation rather than the correct
1128 * result. In this circumstance, the client will throw the exception:
1129 *
1130 * TApplicationException: Wrong method namea
1131 */
1132 /**
1133 * I32 TEST
1134 */
1135 cout << "re-test testI32(-1)" << flush;
1136 int i32 = testClient.testI32(-1);
1137 cout << " = " << i32 << endl;
1138 if (i32 != -1)
1139 return_code |= ERR_BASETYPES;
1140
1141 cout << endl << "All tests done." << endl << flush;
1142
1143 uint64_t stop = now();
1144 uint64_t tot = stop - start;
1145
1146 cout << "Total time: " << stop - start << " us" << endl;
1147
1148 time_tot += tot;
1149 if (time_min == 0 || tot < time_min) {
1150 time_min = tot;
1151 }
1152 if (tot > time_max) {
1153 time_max = tot;
1154 }
1155
1156 cout << flush;
1157 transport->close();
1158 }
1159
1160
1161 uint64_t time_avg = time_tot / numTests;
1162
1163 cout << "Min time: " << time_min << " us" << endl;
1164 cout << "Max time: " << time_max << " us" << endl;
1165 cout << "Avg time: " << time_avg << " us" << endl;
1166
1167 return return_code;
1168 }
1169
binary_fill(std::string & str,string::size_type siz)1170 void binary_fill(std::string& str, string::size_type siz)
1171 {
1172 static const signed char bin_data[256]
1173 = {-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114,
1174 -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99,
1175 -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84,
1176 -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69,
1177 -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54,
1178 -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39,
1179 -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24,
1180 -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9,
1181 -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6,
1182 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
1183 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
1184 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
1185 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
1186 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
1187 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
1188 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
1189 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
1190 127};
1191
1192 str.resize(siz);
1193 char *ptr = &str[0];
1194 string::size_type pos = 0;
1195 for (string::size_type i = 0; i < siz; ++i)
1196 {
1197 if (pos == 255) { pos = 0; } else { ++pos; }
1198 *ptr++ = bin_data[pos];
1199 }
1200 }
1201
binary_test(ThriftTestClient & testClient,string::size_type siz)1202 int binary_test(ThriftTestClient& testClient, string::size_type siz)
1203 {
1204 string bin_request;
1205 string bin_result;
1206
1207 cout << "testBinary(siz = " << siz << ")" << endl;
1208 binary_fill(bin_request, siz);
1209 try {
1210 testClient.testBinary(bin_result, bin_request);
1211
1212 if (bin_request.size() != bin_result.size()) {
1213 cout << "*** FAILED: request size " << bin_request.size() << "; result size " << bin_result.size() << endl;
1214 return ERR_BASETYPES;
1215 }
1216
1217 for (string::size_type i = 0; i < siz; ++i) {
1218 if (bin_request.at(i) != bin_result.at(i)) {
1219 cout << "*** FAILED: at position " << i << " request[i] is h" << hex << bin_request.at(i) << " result[i] is h" << hex << bin_result.at(i) << endl;
1220 return ERR_BASETYPES;
1221 }
1222 }
1223 } catch (TTransportException&) {
1224 throw;
1225 } catch (exception& ex) {
1226 cout << "*** FAILED ***" << endl << ex.what() << endl;
1227 return ERR_BASETYPES;
1228 }
1229
1230 return 0;
1231 }
1232