test_sync_session_present.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /*******************************************************************************
  2. * Copyright (c) 2009, 2020 IBM Corp. and others
  3. *
  4. * All rights reserved. This program and the accompanying materials
  5. * are made available under the terms of the Eclipse Public License v2.0
  6. * and Eclipse Distribution License v1.0 which accompany this distribution.
  7. *
  8. * The Eclipse Public License is available at
  9. * https://www.eclipse.org/legal/epl-2.0/
  10. * and the Eclipse Distribution License is available at
  11. * http://www.eclipse.org/org/documents/edl-v10.php.
  12. *
  13. * Contributors:
  14. * Ian Craggs - Test program utilities
  15. * Milan Tucic - Session present test1
  16. *******************************************************************************/
  17. /**
  18. * @file
  19. * Tests for the MQ Telemetry MQTT C client
  20. */
  21. #include "MQTTClient.h"
  22. #include <string.h>
  23. #include <stdlib.h>
  24. #if !defined(_WINDOWS)
  25. #include <sys/time.h>
  26. #include <sys/socket.h>
  27. #include <unistd.h>
  28. #include <errno.h>
  29. #else
  30. #include <windows.h>
  31. #define setenv(a, b, c) _putenv_s(a, b)
  32. #endif
  33. #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
  34. void usage(void)
  35. {
  36. printf("help!!\n");
  37. exit(EXIT_FAILURE);
  38. }
  39. struct Options
  40. {
  41. char* connection; /**< connection to system under test. */
  42. char** haconnections;
  43. char* proxy_connection;
  44. char* client_id;
  45. char* username;
  46. char* password;
  47. char* log;
  48. int hacount;
  49. int verbose;
  50. int test_no;
  51. int MQTTVersion;
  52. int iterations;
  53. int reconnect_period;
  54. } options =
  55. {
  56. "tcp://mqtt.eclipse.org:1883",
  57. NULL,
  58. "tcp://localhost:1883",
  59. "cli/test",
  60. NULL,
  61. NULL,
  62. NULL,
  63. 0,
  64. 0,
  65. 0,
  66. MQTTVERSION_DEFAULT,
  67. 1,
  68. 3
  69. };
  70. void getopts(int argc, char** argv)
  71. {
  72. int count = 1;
  73. while (count < argc)
  74. {
  75. if (strcmp(argv[count], "--test_no") == 0)
  76. {
  77. if (++count < argc)
  78. options.test_no = atoi(argv[count]);
  79. else
  80. usage();
  81. }
  82. else if (strcmp(argv[count], "--connection") == 0)
  83. {
  84. if (++count < argc)
  85. {
  86. options.connection = argv[count];
  87. printf("\nSetting connection to %s\n", options.connection);
  88. }
  89. else
  90. usage();
  91. }
  92. else if (strcmp(argv[count], "--haconnections") == 0)
  93. {
  94. if (++count < argc)
  95. {
  96. char* tok = strtok(argv[count], " ");
  97. options.hacount = 0;
  98. options.haconnections = malloc(sizeof(char*) * 5);
  99. while (tok)
  100. {
  101. options.haconnections[options.hacount] = malloc(strlen(tok) + 1);
  102. strcpy(options.haconnections[options.hacount], tok);
  103. options.hacount++;
  104. tok = strtok(NULL, " ");
  105. }
  106. }
  107. else
  108. usage();
  109. }
  110. else if (strcmp(argv[count], "--proxy_connection") == 0)
  111. {
  112. if (++count < argc)
  113. options.proxy_connection = argv[count];
  114. else
  115. usage();
  116. }
  117. else if (strcmp(argv[count], "--client_id") == 0)
  118. {
  119. if (++count < argc)
  120. options.client_id = argv[count];
  121. else
  122. usage();
  123. }
  124. else if (strcmp(argv[count], "--username") == 0)
  125. {
  126. if (++count < argc)
  127. options.username = argv[count];
  128. else
  129. usage();
  130. }
  131. else if (strcmp(argv[count], "--password") == 0)
  132. {
  133. if (++count < argc)
  134. options.password = argv[count];
  135. else
  136. usage();
  137. }
  138. else if (strcmp(argv[count], "--log") == 0)
  139. {
  140. if (++count < argc)
  141. options.log = argv[count];
  142. else
  143. usage();
  144. }
  145. else if (strcmp(argv[count], "--MQTTversion") == 0)
  146. {
  147. if (++count < argc)
  148. {
  149. options.MQTTVersion = atoi(argv[count]);
  150. printf("setting MQTT version to %d\n", options.MQTTVersion);
  151. }
  152. else
  153. usage();
  154. }
  155. else if (strcmp(argv[count], "--iterations") == 0)
  156. {
  157. if (++count < argc)
  158. options.iterations = atoi(argv[count]);
  159. else
  160. usage();
  161. }
  162. else if (strcmp(argv[count], "--reconnection_period") == 0)
  163. {
  164. if (++count < argc)
  165. options.reconnect_period = atoi(argv[count]);
  166. else
  167. usage();
  168. }
  169. else if (strcmp(argv[count], "--verbose") == 0)
  170. {
  171. options.verbose = 1;
  172. printf("\nSetting verbose on\n");
  173. }
  174. count++;
  175. }
  176. }
  177. #define LOGA_DEBUG 0
  178. #define LOGA_INFO 1
  179. #include <stdarg.h>
  180. #include <time.h>
  181. #include <sys/timeb.h>
  182. void MyLog(int LOGA_level, char* format, ...)
  183. {
  184. static char msg_buf[256];
  185. va_list args;
  186. #if defined(_WIN32) || defined(_WINDOWS)
  187. struct timeb ts;
  188. #else
  189. struct timeval ts;
  190. #endif
  191. struct tm timeinfo;
  192. if (LOGA_level == LOGA_DEBUG && options.verbose == 0)
  193. return;
  194. #if defined(_WIN32) || defined(_WINDOWS)
  195. ftime(&ts);
  196. localtime_s(&timeinfo, &ts.time);
  197. #else
  198. gettimeofday(&ts, NULL);
  199. localtime_r(&ts.tv_sec, &timeinfo);
  200. #endif
  201. strftime(msg_buf, 80, "%Y%m%d %H%M%S", &timeinfo);
  202. #if defined(_WIN32) || defined(_WINDOWS)
  203. sprintf(&msg_buf[strlen(msg_buf)], ".%.3hu ", ts.millitm);
  204. #else
  205. sprintf(&msg_buf[strlen(msg_buf)], ".%.3lu ", ts.tv_usec / 1000);
  206. #endif
  207. va_start(args, format);
  208. vsnprintf(&msg_buf[strlen(msg_buf)], sizeof(msg_buf) - strlen(msg_buf), format, args);
  209. va_end(args);
  210. printf("%s\n", msg_buf);
  211. fflush(stdout);
  212. }
  213. #if defined(_WIN32) || defined(_WINDOWS)
  214. #define mqsleep(A) Sleep(1000*A)
  215. #define START_TIME_TYPE DWORD
  216. static DWORD start_time = 0;
  217. START_TIME_TYPE start_clock(void)
  218. {
  219. return GetTickCount();
  220. }
  221. #elif defined(AIX)
  222. #define mqsleep sleep
  223. #define START_TIME_TYPE struct timespec
  224. START_TIME_TYPE start_clock(void)
  225. {
  226. static struct timespec start;
  227. clock_gettime(CLOCK_REALTIME, &start);
  228. return start;
  229. }
  230. #else
  231. #define mqsleep sleep
  232. #define START_TIME_TYPE struct timeval
  233. /* TODO - unused - remove? static struct timeval start_time; */
  234. START_TIME_TYPE start_clock(void)
  235. {
  236. struct timeval start_time;
  237. gettimeofday(&start_time, NULL);
  238. return start_time;
  239. }
  240. #endif
  241. #if defined(_WIN32)
  242. long elapsed(START_TIME_TYPE start_time)
  243. {
  244. return GetTickCount() - start_time;
  245. }
  246. #elif defined(AIX)
  247. #define assert(a)
  248. long elapsed(struct timespec start)
  249. {
  250. struct timespec now, res;
  251. clock_gettime(CLOCK_REALTIME, &now);
  252. ntimersub(now, start, res);
  253. return (res.tv_sec)*1000L + (res.tv_nsec)/1000000L;
  254. }
  255. #else
  256. long elapsed(START_TIME_TYPE start_time)
  257. {
  258. struct timeval now, res;
  259. gettimeofday(&now, NULL);
  260. timersub(&now, &start_time, &res);
  261. return (res.tv_sec)*1000 + (res.tv_usec)/1000;
  262. }
  263. #endif
  264. #define assert(a, b, c, d) myassert(__FILE__, __LINE__, a, b, c, d)
  265. #define assert1(a, b, c, d, e) myassert(__FILE__, __LINE__, a, b, c, d, e)
  266. int tests = 0;
  267. int failures = 0;
  268. FILE* xml;
  269. START_TIME_TYPE global_start_time;
  270. char output[3000];
  271. char* cur_output = output;
  272. void write_test_result(void)
  273. {
  274. long duration = elapsed(global_start_time);
  275. fprintf(xml, " time=\"%ld.%.3ld\" >\n", duration / 1000, duration % 1000);
  276. if (cur_output != output)
  277. {
  278. fprintf(xml, "%s", output);
  279. cur_output = output;
  280. }
  281. fprintf(xml, "</testcase>\n");
  282. }
  283. void myassert(char* filename, int lineno, char* description, int value, char* format, ...)
  284. {
  285. ++tests;
  286. if (!value)
  287. {
  288. va_list args;
  289. ++failures;
  290. MyLog(LOGA_INFO, "Assertion failed, file %s, line %d, description: %s\n", filename, lineno, description);
  291. va_start(args, format);
  292. vprintf(format, args);
  293. va_end(args);
  294. cur_output += sprintf(cur_output, "<failure type=\"%s\">file %s, line %d </failure>\n",
  295. description, filename, lineno);
  296. }
  297. else
  298. MyLog(LOGA_DEBUG, "Assertion succeeded, file %s, line %d, description: %s", filename, lineno, description);
  299. }
  300. void mysleep(int seconds)
  301. {
  302. #if defined(_WIN32)
  303. Sleep(1000L*seconds);
  304. #else
  305. sleep(seconds);
  306. #endif
  307. }
  308. /*********************************************************************
  309. Test 1: clean session and reconnect with session present
  310. *********************************************************************/
  311. MQTTClient test1_c1;
  312. void test1_connectionLost(void* context, char* cause)
  313. {
  314. printf("Callback: connection lost\n");
  315. }
  316. void test1_deliveryComplete(void* context, MQTTClient_deliveryToken token)
  317. {
  318. printf("Callback: publish complete for token %d\n", token);
  319. }
  320. int test1_messageArrived(void* context, char* topicName, int topicLen, MQTTClient_message* m)
  321. {
  322. MQTTClient c = (MQTTClient)context;
  323. printf("Callback: message received on topic '%s' is '%.*s'.\n", topicName, m->payloadlen, (char*)(m->payload));
  324. MQTTClient_free(topicName);
  325. MQTTClient_freeMessage(&m);
  326. return 1;
  327. }
  328. int test1(struct Options options)
  329. {
  330. char* testname = "test1";
  331. MQTTClient_connectOptions opts = MQTTClient_connectOptions_initializer;
  332. int rc;
  333. failures = 0;
  334. MyLog(LOGA_INFO, "Starting test 1 - clean session and reconnect with session present");
  335. fprintf(xml, "<testcase classname=\"test1\" name=\"connectionLost and will messages\"");
  336. global_start_time = start_clock();
  337. opts.keepAliveInterval = 60;
  338. opts.cleansession = 1;
  339. opts.MQTTVersion = MQTTVERSION_3_1_1;
  340. if (options.haconnections != NULL)
  341. {
  342. opts.serverURIs = options.haconnections;
  343. opts.serverURIcount = options.hacount;
  344. }
  345. if (options.username)
  346. {
  347. opts.username = options.username;
  348. }
  349. if (options.password)
  350. {
  351. opts.password = options.password;
  352. }
  353. rc = MQTTClient_create(&test1_c1, options.connection, options.client_id, MQTTCLIENT_PERSISTENCE_DEFAULT, NULL);
  354. assert("good rc from create", rc == MQTTCLIENT_SUCCESS, "rc was %d\n", rc);
  355. if (rc != MQTTCLIENT_SUCCESS)
  356. goto exit;
  357. rc = MQTTClient_setCallbacks(test1_c1, (void*)test1_c1, test1_connectionLost, test1_messageArrived, test1_deliveryComplete);
  358. assert("good rc from setCallbacks", rc == MQTTCLIENT_SUCCESS, "rc was %d\n", rc);
  359. if (rc != MQTTCLIENT_SUCCESS)
  360. goto exit;
  361. /* Connect to the broker with clean session = true */
  362. rc = MQTTClient_connect(test1_c1, &opts);
  363. assert("good rc from connect with clean session = true", rc == MQTTCLIENT_SUCCESS, "rc was %d\n", rc);
  364. if (rc != MQTTCLIENT_SUCCESS)
  365. goto exit;
  366. assert("connected, session cleaned", opts.returned.sessionPresent == 0,
  367. "opts.returned.sessionPresent = %d\n", opts.returned.sessionPresent);
  368. /* Disconnect */
  369. rc = MQTTClient_disconnect(test1_c1, 1000);
  370. assert("good rc from disconnect", rc == MQTTCLIENT_SUCCESS, "rc was %d\n", rc);
  371. if (rc != MQTTCLIENT_SUCCESS)
  372. goto exit;
  373. MyLog(LOGA_INFO, "Sleeping after session cleaned %d s ...", options.reconnect_period);
  374. mysleep(options.reconnect_period);
  375. /* Connect to the broker with clean session = false */
  376. opts.cleansession = 0;
  377. rc = MQTTClient_connect(test1_c1, &opts);
  378. assert("good rc from connect with clean session = false", rc == MQTTCLIENT_SUCCESS, "rc was %d\n", rc);
  379. if (rc != MQTTCLIENT_SUCCESS)
  380. goto exit;
  381. assert("connected, session clean", opts.returned.sessionPresent == 0,
  382. "opts.returned.sessionPresent = %d\n", opts.returned.sessionPresent);
  383. /* Disconnect */
  384. rc = MQTTClient_disconnect(test1_c1, 1000);
  385. assert("good rc from disconnect", rc == MQTTCLIENT_SUCCESS, "rc was %d\n", rc);
  386. if (rc != MQTTCLIENT_SUCCESS)
  387. goto exit;
  388. MyLog(LOGA_INFO, "Sleeping after session persist %d s ...", options.reconnect_period);
  389. mysleep(options.reconnect_period);
  390. /* Connect to the broker with clean session = false, expected to have session */
  391. opts.cleansession = 0;
  392. rc = MQTTClient_connect(test1_c1, &opts);
  393. assert("good rc from second connect with clean session = false", rc == MQTTCLIENT_SUCCESS, "rc was %d\n", rc);
  394. if (rc != MQTTCLIENT_SUCCESS)
  395. goto exit;
  396. assert("connected, session present", opts.returned.sessionPresent == 1,
  397. "opts.returned.sessionPresent = %d\n", opts.returned.sessionPresent);
  398. MQTTClient_destroy(&test1_c1);
  399. exit:
  400. MyLog(LOGA_INFO, "%s: test %s. %d tests run, %d failures.\n",
  401. (failures == 0) ? "passed" : "failed", testname, tests, failures);
  402. write_test_result();
  403. return failures;
  404. }
  405. int main(int argc, char** argv)
  406. {
  407. int rc = 0;
  408. int (*tests[])() = {NULL, test1};
  409. int i;
  410. unsigned test_i;
  411. xml = fopen("TEST-test1.xml", "w");
  412. fprintf(xml, "<testsuite name=\"test1\" tests=\"%d\">\n", (int)(ARRAY_SIZE(tests) - 1));
  413. getopts(argc, argv);
  414. if (options.log)
  415. {
  416. setenv("MQTT_C_CLIENT_TRACE", "ON", 1);
  417. setenv("MQTT_C_CLIENT_TRACE_LEVEL", options.log, 1);
  418. }
  419. MyLog(LOGA_INFO, "Running %d iteration(s)", options.iterations);
  420. for (i = 0; i < options.iterations; ++i)
  421. {
  422. if (options.test_no == 0)
  423. { /* run all the tests */
  424. for (test_i = 1; test_i < ARRAY_SIZE(tests); ++test_i)
  425. rc += tests[test_i](options); /* return number of failures. 0 = test succeeded */
  426. }
  427. else
  428. rc = tests[options.test_no](options); /* run just the selected test */
  429. }
  430. if (rc == 0)
  431. MyLog(LOGA_INFO, "verdict pass");
  432. else
  433. MyLog(LOGA_INFO, "verdict fail");
  434. fprintf(xml, "</testsuite>\n");
  435. fclose(xml);
  436. return rc;
  437. }