Line data Source code
1 : /**
2 : * @file test_tl_skip_message_functional.c
3 : * @brief End-to-end iteration of a heavily-decorated Message object.
4 : *
5 : * The unit tests exercise each skipper in isolation. This functional
6 : * test builds one Message carrying every supported trailer flag at
7 : * once (media + reply_markup + entities + views + forwards + replies +
8 : * restriction_reason + reactions + ttl_period + factcheck), feeds it
9 : * to tl_skip_message, and asserts the reader lands exactly on the
10 : * bytes that follow. The goal is to catch dispatch-order bugs and
11 : * cross-skipper interactions that the per-unit tests cannot see.
12 : */
13 :
14 : #include "test_helpers.h"
15 : #include "tl_serial.h"
16 : #include "tl_registry.h"
17 : #include "tl_skip.h"
18 :
19 : #include <stdint.h>
20 : #include <string.h>
21 :
22 : /* CRCs not re-exposed from tl_skip.c. */
23 : #define CRC_messageMediaEmpty_t 0x3ded6320u
24 : #define CRC_replyInlineMarkup_t 0x48a30254u
25 : #define CRC_keyboardButtonRow_t 0x77608b83u
26 : #define CRC_keyboardButtonCallback_t 0x35bbdb6bu
27 : #define CRC_messageEntityBold_t 0xbd610bc9u
28 : #define CRC_messageReplies_t 0x83d60fc2u
29 : #define CRC_restrictionReason_t 0xd072acb4u
30 : #define CRC_messageReactions_t 0x4f2b9479u
31 : #define CRC_reactionCount_t 0xa3d1cb80u
32 : #define CRC_reactionEmoji_t 0x1b2286b8u
33 : #define CRC_factCheck_t 0xb89bfccfu
34 : #define CRC_textWithEntities_t 0x751f3146u
35 :
36 : /* Message flag constants. */
37 : #define FLAG_REPLY_MARKUP (1u << 6)
38 : #define FLAG_ENTITIES (1u << 7)
39 : #define FLAG_FROM_ID (1u << 8)
40 : #define FLAG_MEDIA (1u << 9)
41 : #define FLAG_VIEWS_FWDS (1u << 10)
42 : #define FLAG_EDIT_DATE (1u << 15)
43 : #define FLAG_POST_AUTHOR (1u << 16)
44 : #define FLAG_GROUPED_ID (1u << 17)
45 : #define FLAG_REACTIONS (1u << 20)
46 : #define FLAG_RESTRICTION (1u << 22)
47 : #define FLAG_REPLIES (1u << 23)
48 : #define FLAG_TTL_PERIOD (1u << 25)
49 : #define FLAG2_FACTCHECK (1u << 3)
50 :
51 6 : static void write_message_kitchen_sink(TlWriter *w) {
52 6 : uint32_t flags = FLAG_FROM_ID | FLAG_MEDIA | FLAG_REPLY_MARKUP
53 : | FLAG_ENTITIES | FLAG_VIEWS_FWDS | FLAG_EDIT_DATE
54 : | FLAG_POST_AUTHOR | FLAG_GROUPED_ID | FLAG_REACTIONS
55 : | FLAG_RESTRICTION | FLAG_REPLIES | FLAG_TTL_PERIOD;
56 6 : uint32_t flags2 = FLAG2_FACTCHECK;
57 :
58 6 : tl_write_uint32(w, TL_message);
59 6 : tl_write_uint32(w, flags);
60 6 : tl_write_uint32(w, flags2);
61 6 : tl_write_int32 (w, 1001); /* message id */
62 :
63 : /* from_id:Peer + peer_id:Peer */
64 6 : tl_write_uint32(w, TL_peerUser); tl_write_int64(w, 42LL);
65 6 : tl_write_uint32(w, TL_peerChannel); tl_write_int64(w, 5001LL);
66 :
67 6 : tl_write_int32 (w, 1700000000); /* date */
68 6 : tl_write_string(w, "Kitchen-sink post"); /* message */
69 :
70 : /* media: messageMediaEmpty */
71 6 : tl_write_uint32(w, CRC_messageMediaEmpty_t);
72 :
73 : /* reply_markup: replyInlineMarkup + one row with a callback button. */
74 6 : tl_write_uint32(w, CRC_replyInlineMarkup_t);
75 6 : tl_write_uint32(w, TL_vector); tl_write_uint32(w, 1);
76 6 : tl_write_uint32(w, CRC_keyboardButtonRow_t);
77 6 : tl_write_uint32(w, TL_vector); tl_write_uint32(w, 1);
78 6 : tl_write_uint32(w, CRC_keyboardButtonCallback_t);
79 6 : tl_write_uint32(w, 0); /* inner flags */
80 6 : tl_write_string(w, "Click me");
81 6 : tl_write_string(w, "payload");
82 :
83 : /* entities: one bold */
84 6 : tl_write_uint32(w, TL_vector); tl_write_uint32(w, 1);
85 6 : tl_write_uint32(w, CRC_messageEntityBold_t);
86 6 : tl_write_int32 (w, 0); tl_write_int32(w, 7);
87 :
88 : /* views + forwards */
89 6 : tl_write_int32 (w, 12345);
90 6 : tl_write_int32 (w, 67);
91 :
92 : /* replies: messageReplies flags=0, two int32 */
93 6 : tl_write_uint32(w, CRC_messageReplies_t);
94 6 : tl_write_uint32(w, 0);
95 6 : tl_write_int32 (w, 9);
96 6 : tl_write_int32 (w, 200);
97 :
98 : /* edit_date */
99 6 : tl_write_int32 (w, 1700001111);
100 :
101 : /* post_author */
102 6 : tl_write_string(w, "Anonymous");
103 :
104 : /* grouped_id */
105 6 : tl_write_int64 (w, 0xDEADBEEFLL);
106 :
107 : /* reactions: one emoji reaction with 3 voters */
108 6 : tl_write_uint32(w, CRC_messageReactions_t);
109 6 : tl_write_uint32(w, 0);
110 6 : tl_write_uint32(w, TL_vector); tl_write_uint32(w, 1);
111 6 : tl_write_uint32(w, CRC_reactionCount_t);
112 6 : tl_write_uint32(w, 0); /* no chosen_order */
113 6 : tl_write_uint32(w, CRC_reactionEmoji_t);
114 6 : tl_write_string(w, "\xf0\x9f\x94\xa5"); /* 🔥 */
115 6 : tl_write_int32 (w, 3);
116 :
117 : /* restriction_reason: Vector<RestrictionReason>, one entry */
118 6 : tl_write_uint32(w, TL_vector); tl_write_uint32(w, 1);
119 6 : tl_write_uint32(w, CRC_restrictionReason_t);
120 6 : tl_write_string(w, "android");
121 6 : tl_write_string(w, "sensitive");
122 6 : tl_write_string(w, "Age-restricted");
123 :
124 : /* ttl_period */
125 6 : tl_write_int32 (w, 3600);
126 :
127 : /* factcheck: flags.1 with country + TextWithEntities, hash */
128 6 : tl_write_uint32(w, CRC_factCheck_t);
129 6 : tl_write_uint32(w, (1u << 1));
130 6 : tl_write_string(w, "HU");
131 6 : tl_write_uint32(w, CRC_textWithEntities_t);
132 6 : tl_write_string(w, "verified claim");
133 6 : tl_write_uint32(w, TL_vector); tl_write_uint32(w, 0);
134 6 : tl_write_int64 (w, 0xABCDEF0012345678LL);
135 6 : }
136 :
137 2 : static void test_kitchen_sink_message_iterates_fully(void) {
138 2 : TlWriter w; tl_writer_init(&w);
139 2 : write_message_kitchen_sink(&w);
140 2 : tl_write_int32(&w, 0x0BAD1DEA); /* sentinel trailer */
141 :
142 2 : TlReader r = tl_reader_init(w.data, w.len);
143 2 : ASSERT(tl_skip_message(&r) == 0,
144 : "multi-flag Message skipped cleanly");
145 2 : ASSERT(tl_read_int32(&r) == 0x0BAD1DEA,
146 : "cursor landed exactly on sentinel after Message");
147 2 : ASSERT(r.pos == r.len, "reader fully consumed");
148 2 : tl_writer_free(&w);
149 : }
150 :
151 : /* Two Messages in sequence: prove that hitting any trailer does not
152 : * desynchronise the reader for the next one. */
153 2 : static void test_two_messages_in_a_row(void) {
154 2 : TlWriter w; tl_writer_init(&w);
155 2 : write_message_kitchen_sink(&w);
156 2 : write_message_kitchen_sink(&w);
157 2 : tl_write_int32(&w, 0xCAFEBABE);
158 :
159 2 : TlReader r = tl_reader_init(w.data, w.len);
160 2 : ASSERT(tl_skip_message(&r) == 0, "first Message skipped");
161 2 : ASSERT(tl_skip_message(&r) == 0, "second Message skipped");
162 2 : ASSERT(tl_read_int32(&r) == (int32_t)0xCAFEBABE, "trailer reached");
163 2 : tl_writer_free(&w);
164 : }
165 :
166 2 : void run_tl_skip_message_functional_tests(void) {
167 2 : RUN_TEST(test_kitchen_sink_message_iterates_fully);
168 2 : RUN_TEST(test_two_messages_in_a_row);
169 2 : }
|