test_tables.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. * ***** BEGIN LICENSE BLOCK *****
  3. * Version: MIT
  4. *
  5. * Portions created by Alan Antonuk are Copyright (c) 2012-2013
  6. * Alan Antonuk. All Rights Reserved.
  7. *
  8. * Portions created by VMware are Copyright (c) 2007-2012 VMware, Inc.
  9. * All Rights Reserved.
  10. *
  11. * Portions created by Tony Garnock-Jones are Copyright (c) 2009-2010
  12. * VMware, Inc. and Tony Garnock-Jones. All Rights Reserved.
  13. *
  14. * Permission is hereby granted, free of charge, to any person
  15. * obtaining a copy of this software and associated documentation
  16. * files (the "Software"), to deal in the Software without
  17. * restriction, including without limitation the rights to use, copy,
  18. * modify, merge, publish, distribute, sublicense, and/or sell copies
  19. * of the Software, and to permit persons to whom the Software is
  20. * furnished to do so, subject to the following conditions:
  21. *
  22. * The above copyright notice and this permission notice shall be
  23. * included in all copies or substantial portions of the Software.
  24. *
  25. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  26. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  27. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  28. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  29. * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  30. * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  31. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  32. * SOFTWARE.
  33. * ***** END LICENSE BLOCK *****
  34. */
  35. #ifdef _MSC_VER
  36. #define _USE_MATH_DEFINES
  37. #define _CRT_SECURE_NO_WARNINGS
  38. #endif
  39. #include <errno.h>
  40. #include <stdarg.h>
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <string.h>
  44. #include <inttypes.h>
  45. #include <amqp.h>
  46. #include <math.h>
  47. void die(const char *fmt, ...) {
  48. va_list ap;
  49. va_start(ap, fmt);
  50. vfprintf(stderr, fmt, ap);
  51. va_end(ap);
  52. fprintf(stderr, "\n");
  53. abort();
  54. }
  55. static void dump_indent(int indent, FILE *out) {
  56. int i;
  57. for (i = 0; i < indent; i++) {
  58. fputc(' ', out);
  59. }
  60. }
  61. static void dump_value(int indent, amqp_field_value_t v, FILE *out) {
  62. int i;
  63. dump_indent(indent, out);
  64. fputc(v.kind, out);
  65. switch (v.kind) {
  66. case AMQP_FIELD_KIND_BOOLEAN:
  67. fputs(v.value.boolean ? " true\n" : " false\n", out);
  68. break;
  69. case AMQP_FIELD_KIND_I8:
  70. fprintf(out, " %" PRId8 "\n", v.value.i8);
  71. break;
  72. case AMQP_FIELD_KIND_U8:
  73. fprintf(out, " %" PRIu8 "\n", v.value.u8);
  74. break;
  75. case AMQP_FIELD_KIND_I16:
  76. fprintf(out, " %" PRId16 "\n", v.value.i16);
  77. break;
  78. case AMQP_FIELD_KIND_U16:
  79. fprintf(out, " %" PRIu16 "\n", v.value.u16);
  80. break;
  81. case AMQP_FIELD_KIND_I32:
  82. fprintf(out, " %" PRId32 "\n", v.value.i32);
  83. break;
  84. case AMQP_FIELD_KIND_U32:
  85. fprintf(out, " %" PRIu32 "\n", v.value.u32);
  86. break;
  87. case AMQP_FIELD_KIND_I64:
  88. fprintf(out, " %" PRId64 "\n", v.value.i64);
  89. break;
  90. case AMQP_FIELD_KIND_F32:
  91. fprintf(out, " %g\n", (double)v.value.f32);
  92. break;
  93. case AMQP_FIELD_KIND_F64:
  94. fprintf(out, " %g\n", v.value.f64);
  95. break;
  96. case AMQP_FIELD_KIND_DECIMAL:
  97. fprintf(out, " %u:::%u\n", v.value.decimal.decimals,
  98. v.value.decimal.value);
  99. break;
  100. case AMQP_FIELD_KIND_UTF8:
  101. fprintf(out, " %.*s\n", (int)v.value.bytes.len,
  102. (char *)v.value.bytes.bytes);
  103. break;
  104. case AMQP_FIELD_KIND_BYTES:
  105. fputc(' ', out);
  106. for (i = 0; i < (int)v.value.bytes.len; i++) {
  107. fprintf(out, "%02x", ((char *)v.value.bytes.bytes)[i]);
  108. }
  109. fputc('\n', out);
  110. break;
  111. case AMQP_FIELD_KIND_ARRAY:
  112. fputc('\n', out);
  113. for (i = 0; i < v.value.array.num_entries; i++) {
  114. dump_value(indent + 2, v.value.array.entries[i], out);
  115. }
  116. break;
  117. case AMQP_FIELD_KIND_TIMESTAMP:
  118. fprintf(out, " %" PRIu64 "\n", v.value.u64);
  119. break;
  120. case AMQP_FIELD_KIND_TABLE:
  121. fputc('\n', out);
  122. for (i = 0; i < v.value.table.num_entries; i++) {
  123. dump_indent(indent + 2, out);
  124. fprintf(out, "%.*s ->\n", (int)v.value.table.entries[i].key.len,
  125. (char *)v.value.table.entries[i].key.bytes);
  126. dump_value(indent + 4, v.value.table.entries[i].value, out);
  127. }
  128. break;
  129. case AMQP_FIELD_KIND_VOID:
  130. fputc('\n', out);
  131. break;
  132. default:
  133. fprintf(out, "???\n");
  134. break;
  135. }
  136. }
  137. static void test_dump_value(FILE *out) {
  138. amqp_table_entry_t entries[8];
  139. amqp_table_t table;
  140. amqp_field_value_t val;
  141. entries[0].key = amqp_cstring_bytes("zebra");
  142. entries[0].value.kind = AMQP_FIELD_KIND_UTF8;
  143. entries[0].value.value.bytes = amqp_cstring_bytes("last");
  144. entries[1].key = amqp_cstring_bytes("aardvark");
  145. entries[1].value.kind = AMQP_FIELD_KIND_UTF8;
  146. entries[1].value.value.bytes = amqp_cstring_bytes("first");
  147. entries[2].key = amqp_cstring_bytes("middle");
  148. entries[2].value.kind = AMQP_FIELD_KIND_UTF8;
  149. entries[2].value.value.bytes = amqp_cstring_bytes("third");
  150. entries[3].key = amqp_cstring_bytes("number");
  151. entries[3].value.kind = AMQP_FIELD_KIND_I32;
  152. entries[3].value.value.i32 = 1234;
  153. entries[4].key = amqp_cstring_bytes("decimal");
  154. entries[4].value.kind = AMQP_FIELD_KIND_DECIMAL;
  155. entries[4].value.value.decimal.decimals = 2;
  156. entries[4].value.value.decimal.value = 1234;
  157. entries[5].key = amqp_cstring_bytes("time");
  158. entries[5].value.kind = AMQP_FIELD_KIND_TIMESTAMP;
  159. entries[5].value.value.u64 = 1234123412341234;
  160. entries[6].key = amqp_cstring_bytes("beta");
  161. entries[6].value.kind = AMQP_FIELD_KIND_UTF8;
  162. entries[6].value.value.bytes = amqp_cstring_bytes("second");
  163. entries[7].key = amqp_cstring_bytes("wombat");
  164. entries[7].value.kind = AMQP_FIELD_KIND_UTF8;
  165. entries[7].value.value.bytes = amqp_cstring_bytes("fourth");
  166. table.num_entries = 8;
  167. table.entries = entries;
  168. qsort(table.entries, table.num_entries, sizeof(amqp_table_entry_t),
  169. &amqp_table_entry_cmp);
  170. val.kind = AMQP_FIELD_KIND_TABLE;
  171. val.value.table = table;
  172. dump_value(0, val, out);
  173. }
  174. static uint8_t pre_encoded_table[] = {
  175. 0x00, 0x00, 0x00, 0xff, 0x07, 0x6c, 0x6f, 0x6e, 0x67, 0x73, 0x74, 0x72,
  176. 0x53, 0x00, 0x00, 0x00, 0x15, 0x48, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73,
  177. 0x20, 0x61, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x74, 0x72, 0x69,
  178. 0x6e, 0x67, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x69, 0x6e, 0x74,
  179. 0x49, 0x00, 0x00, 0x30, 0x39, 0x07, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61,
  180. 0x6c, 0x44, 0x03, 0x00, 0x01, 0xe2, 0x40, 0x09, 0x74, 0x69, 0x6d, 0x65,
  181. 0x73, 0x74, 0x61, 0x6d, 0x70, 0x54, 0x00, 0x00, 0x63, 0xee, 0xa0, 0x53,
  182. 0xc1, 0x94, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x46, 0x00, 0x00, 0x00,
  183. 0x1f, 0x03, 0x6f, 0x6e, 0x65, 0x49, 0x00, 0x00, 0xd4, 0x31, 0x03, 0x74,
  184. 0x77, 0x6f, 0x53, 0x00, 0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e,
  185. 0x67, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x04, 0x62, 0x79, 0x74,
  186. 0x65, 0x62, 0xff, 0x04, 0x6c, 0x6f, 0x6e, 0x67, 0x6c, 0x00, 0x00, 0x00,
  187. 0x00, 0x49, 0x96, 0x02, 0xd2, 0x05, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x73,
  188. 0x02, 0x8f, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x74, 0x01, 0x06, 0x62, 0x69,
  189. 0x6e, 0x61, 0x72, 0x79, 0x78, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x20, 0x62,
  190. 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
  191. 0x04, 0x76, 0x6f, 0x69, 0x64, 0x56, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79,
  192. 0x41, 0x00, 0x00, 0x00, 0x17, 0x49, 0x00, 0x00, 0xd4, 0x31, 0x53, 0x00,
  193. 0x00, 0x00, 0x0d, 0x41, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x74,
  194. 0x72, 0x69, 0x6e, 0x67, 0x05, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x66, 0x40,
  195. 0x49, 0x0f, 0xdb, 0x06, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x64, 0x40,
  196. 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18};
  197. static void test_table_codec(FILE *out) {
  198. amqp_pool_t pool;
  199. int result;
  200. amqp_table_entry_t inner_entries[2];
  201. amqp_table_t inner_table;
  202. amqp_field_value_t inner_values[2];
  203. amqp_array_t inner_array;
  204. amqp_table_entry_t entries[14];
  205. amqp_table_t table;
  206. inner_entries[0].key = amqp_cstring_bytes("one");
  207. inner_entries[0].value.kind = AMQP_FIELD_KIND_I32;
  208. inner_entries[0].value.value.i32 = 54321;
  209. inner_entries[1].key = amqp_cstring_bytes("two");
  210. inner_entries[1].value.kind = AMQP_FIELD_KIND_UTF8;
  211. inner_entries[1].value.value.bytes = amqp_cstring_bytes("A long string");
  212. inner_table.num_entries = 2;
  213. inner_table.entries = inner_entries;
  214. inner_values[0].kind = AMQP_FIELD_KIND_I32;
  215. inner_values[0].value.i32 = 54321;
  216. inner_values[1].kind = AMQP_FIELD_KIND_UTF8;
  217. inner_values[1].value.bytes = amqp_cstring_bytes("A long string");
  218. inner_array.num_entries = 2;
  219. inner_array.entries = inner_values;
  220. entries[0].key = amqp_cstring_bytes("longstr");
  221. entries[0].value.kind = AMQP_FIELD_KIND_UTF8;
  222. entries[0].value.value.bytes = amqp_cstring_bytes("Here is a long string");
  223. entries[1].key = amqp_cstring_bytes("signedint");
  224. entries[1].value.kind = AMQP_FIELD_KIND_I32;
  225. entries[1].value.value.i32 = 12345;
  226. entries[2].key = amqp_cstring_bytes("decimal");
  227. entries[2].value.kind = AMQP_FIELD_KIND_DECIMAL;
  228. entries[2].value.value.decimal.decimals = 3;
  229. entries[2].value.value.decimal.value = 123456;
  230. entries[3].key = amqp_cstring_bytes("timestamp");
  231. entries[3].value.kind = AMQP_FIELD_KIND_TIMESTAMP;
  232. entries[3].value.value.u64 = 109876543209876;
  233. entries[4].key = amqp_cstring_bytes("table");
  234. entries[4].value.kind = AMQP_FIELD_KIND_TABLE;
  235. entries[4].value.value.table = inner_table;
  236. entries[5].key = amqp_cstring_bytes("byte");
  237. entries[5].value.kind = AMQP_FIELD_KIND_I8;
  238. entries[5].value.value.i8 = (int8_t)-1;
  239. entries[6].key = amqp_cstring_bytes("long");
  240. entries[6].value.kind = AMQP_FIELD_KIND_I64;
  241. entries[6].value.value.i64 = 1234567890;
  242. entries[7].key = amqp_cstring_bytes("short");
  243. entries[7].value.kind = AMQP_FIELD_KIND_I16;
  244. entries[7].value.value.i16 = 655;
  245. entries[8].key = amqp_cstring_bytes("bool");
  246. entries[8].value.kind = AMQP_FIELD_KIND_BOOLEAN;
  247. entries[8].value.value.boolean = 1;
  248. entries[9].key = amqp_cstring_bytes("binary");
  249. entries[9].value.kind = AMQP_FIELD_KIND_BYTES;
  250. entries[9].value.value.bytes = amqp_cstring_bytes("a binary string");
  251. entries[10].key = amqp_cstring_bytes("void");
  252. entries[10].value.kind = AMQP_FIELD_KIND_VOID;
  253. entries[11].key = amqp_cstring_bytes("array");
  254. entries[11].value.kind = AMQP_FIELD_KIND_ARRAY;
  255. entries[11].value.value.array = inner_array;
  256. entries[12].key = amqp_cstring_bytes("float");
  257. entries[12].value.kind = AMQP_FIELD_KIND_F32;
  258. entries[12].value.value.f32 = (float)M_PI;
  259. entries[13].key = amqp_cstring_bytes("double");
  260. entries[13].value.kind = AMQP_FIELD_KIND_F64;
  261. entries[13].value.value.f64 = M_PI;
  262. table.num_entries = 14;
  263. table.entries = entries;
  264. fprintf(out, "AAAAAAAAAA\n");
  265. {
  266. amqp_field_value_t val;
  267. val.kind = AMQP_FIELD_KIND_TABLE;
  268. val.value.table = table;
  269. dump_value(0, val, out);
  270. }
  271. init_amqp_pool(&pool, 4096);
  272. {
  273. amqp_table_t decoded;
  274. size_t decoding_offset = 0;
  275. amqp_bytes_t decoding_bytes;
  276. decoding_bytes.len = sizeof(pre_encoded_table);
  277. decoding_bytes.bytes = pre_encoded_table;
  278. result =
  279. amqp_decode_table(decoding_bytes, &pool, &decoded, &decoding_offset);
  280. if (result < 0) {
  281. die("Table decoding failed: %s", amqp_error_string2(result));
  282. }
  283. fprintf(out, "BBBBBBBBBB\n");
  284. {
  285. amqp_field_value_t val;
  286. val.kind = AMQP_FIELD_KIND_TABLE;
  287. val.value.table = decoded;
  288. dump_value(0, val, out);
  289. }
  290. }
  291. {
  292. uint8_t encoding_buffer[4096];
  293. amqp_bytes_t encoding_result;
  294. size_t offset = 0;
  295. memset(&encoding_buffer[0], 0, sizeof(encoding_buffer));
  296. encoding_result.len = sizeof(encoding_buffer);
  297. encoding_result.bytes = &encoding_buffer[0];
  298. result = amqp_encode_table(encoding_result, &table, &offset);
  299. if (result < 0) {
  300. die("Table encoding failed: %s", amqp_error_string2(result));
  301. }
  302. if (offset != sizeof(pre_encoded_table))
  303. die("Offset should be %ld, was %ld", (long)sizeof(pre_encoded_table),
  304. (long)offset);
  305. result = memcmp(pre_encoded_table, encoding_buffer, offset);
  306. if (result != 0) {
  307. die("Table encoding differed", result);
  308. }
  309. }
  310. empty_amqp_pool(&pool);
  311. }
  312. #define CHUNK_SIZE 4096
  313. static int compare_files(FILE *f1_in, FILE *f2_in) {
  314. char f1_buf[CHUNK_SIZE];
  315. char f2_buf[CHUNK_SIZE];
  316. int res;
  317. rewind(f1_in);
  318. rewind(f2_in);
  319. for (;;) {
  320. size_t f1_got = fread(f1_buf, 1, CHUNK_SIZE, f1_in);
  321. size_t f2_got = fread(f2_buf, 1, CHUNK_SIZE, f2_in);
  322. res = memcmp(f1_buf, f2_buf, f1_got < f2_got ? f1_got : f2_got);
  323. if (res) {
  324. break;
  325. }
  326. if (f1_got < CHUNK_SIZE || f2_got < CHUNK_SIZE) {
  327. if (f1_got != f2_got) {
  328. res = (f1_got < f2_got ? -1 : 1);
  329. }
  330. break;
  331. }
  332. }
  333. return res;
  334. }
  335. const char *expected_file_name = "tests/test_tables.expected";
  336. int main(void) {
  337. char *srcdir = getenv("srcdir");
  338. FILE *out, *expected = NULL;
  339. char *expected_path;
  340. out = tmpfile();
  341. if (out == NULL) {
  342. die("failed to create temporary file: %s", strerror(errno));
  343. }
  344. test_table_codec(out);
  345. fprintf(out, "----------\n");
  346. test_dump_value(out);
  347. if (srcdir == NULL) {
  348. srcdir = ".";
  349. }
  350. expected_path = malloc(strlen(srcdir) + strlen(expected_file_name) + 2);
  351. if (!expected_path) {
  352. die("out of memory");
  353. }
  354. sprintf(expected_path, "%s/%s", srcdir, expected_file_name);
  355. expected = fopen(expected_path, "r");
  356. if (!expected) {
  357. die("failed to open %s: %s", expected_path, strerror(errno));
  358. }
  359. if (compare_files(expected, out)) {
  360. die("output file did not have expected contents");
  361. }
  362. fclose(expected);
  363. free(expected_path);
  364. fclose(out);
  365. return 0;
  366. }