Line data Source code
1 : /**
2 : * @file test_gzip.c
3 : * @brief Unit tests for gzip_packed unwrapping (rpc_unwrap_gzip).
4 : *
5 : * Tests both the gzip decompression path (using real gzip data)
6 : * and the pass-through path (non-gzip data copied unchanged).
7 : */
8 :
9 : #include "test_helpers.h"
10 : #include "mtproto_rpc.h"
11 : #include "tl_serial.h"
12 : #include "tinf.h"
13 :
14 : #include <string.h>
15 : #include <stdlib.h>
16 :
17 : /* ---- Pre-compressed gzip test data ----
18 : *
19 : * Created by compressing "Hello, MTProto!" (15 bytes) with gzip.
20 : * Generated via: echo -n "Hello, MTProto!" | gzip | xxd -i
21 : */
22 : static const uint8_t gzip_hello[] = {
23 : 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
24 : 0x00, 0x03, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0xd7,
25 : 0x51, 0xf0, 0x0d, 0x09, 0x28, 0xca, 0x2f, 0xc9,
26 : 0x57, 0x04, 0x00, 0x59, 0xf5, 0x5d, 0x03, 0x0f,
27 : 0x00, 0x00, 0x00
28 : };
29 : static const size_t gzip_hello_len = sizeof(gzip_hello);
30 :
31 : /* ---- Tests ---- */
32 :
33 1 : void test_unwrap_gzip_passthrough(void) {
34 : /* Non-gzip data should be copied unchanged */
35 1 : uint8_t data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
36 : uint8_t out[256];
37 1 : size_t out_len = 0;
38 :
39 1 : int rc = rpc_unwrap_gzip(data, sizeof(data), out, sizeof(out), &out_len);
40 1 : ASSERT(rc == 0, "passthrough should succeed");
41 1 : ASSERT(out_len == sizeof(data), "output length should match input");
42 1 : ASSERT(memcmp(out, data, sizeof(data)) == 0, "data should be unchanged");
43 : }
44 :
45 1 : void test_unwrap_gzip_passthrough_constructor(void) {
46 : /* Data with a different constructor should pass through */
47 : TlWriter w;
48 1 : tl_writer_init(&w);
49 1 : tl_write_uint32(&w, 0x05162463); /* CRC_resPQ — not gzip_packed */
50 1 : tl_write_int32(&w, 42);
51 :
52 : uint8_t out[256];
53 1 : size_t out_len = 0;
54 1 : int rc = rpc_unwrap_gzip(w.data, w.len, out, sizeof(out), &out_len);
55 1 : ASSERT(rc == 0, "non-gzip constructor should pass through");
56 1 : ASSERT(out_len == w.len, "output length should match");
57 1 : ASSERT(memcmp(out, w.data, w.len) == 0, "data should be unchanged");
58 1 : tl_writer_free(&w);
59 : }
60 :
61 1 : void test_unwrap_gzip_decompress(void) {
62 : /* Build gzip_packed TL: constructor(4) + bytes(gzip_hello) */
63 : TlWriter w;
64 1 : tl_writer_init(&w);
65 1 : tl_write_uint32(&w, 0x3072cfa1); /* CRC_gzip_packed */
66 1 : tl_write_bytes(&w, gzip_hello, gzip_hello_len);
67 :
68 : uint8_t out[1024];
69 1 : size_t out_len = 0;
70 1 : int rc = rpc_unwrap_gzip(w.data, w.len, out, sizeof(out), &out_len);
71 1 : tl_writer_free(&w);
72 :
73 1 : ASSERT(rc == 0, "gzip decompression should succeed");
74 1 : ASSERT(out_len == 15, "decompressed length should be 15");
75 1 : ASSERT(memcmp(out, "Hello, MTProto!", 15) == 0,
76 : "decompressed data should match original");
77 : }
78 :
79 1 : void test_unwrap_gzip_null_args(void) {
80 : uint8_t buf[16];
81 1 : size_t len = 0;
82 1 : ASSERT(rpc_unwrap_gzip(NULL, 4, buf, 16, &len) == -1, "NULL data should fail");
83 1 : ASSERT(rpc_unwrap_gzip(buf, 4, NULL, 16, &len) == -1, "NULL out should fail");
84 1 : ASSERT(rpc_unwrap_gzip(buf, 4, buf, 16, NULL) == -1, "NULL out_len should fail");
85 : }
86 :
87 1 : void test_unwrap_gzip_short_data(void) {
88 : /* Data shorter than 4 bytes — pass through */
89 1 : uint8_t data[] = { 0xAA, 0xBB };
90 : uint8_t out[16];
91 1 : size_t out_len = 0;
92 1 : int rc = rpc_unwrap_gzip(data, 2, out, sizeof(out), &out_len);
93 1 : ASSERT(rc == 0, "short data should pass through");
94 1 : ASSERT(out_len == 2, "output length should be 2");
95 : }
96 :
97 1 : void test_unwrap_gzip_buffer_too_small(void) {
98 : /* Passthrough with too-small buffer */
99 : uint8_t data[32];
100 1 : memset(data, 0x42, 32);
101 : uint8_t out[8];
102 1 : size_t out_len = 0;
103 1 : int rc = rpc_unwrap_gzip(data, 32, out, 8, &out_len);
104 1 : ASSERT(rc == -1, "buffer too small should fail");
105 : }
106 :
107 1 : void test_unwrap_gzip_corrupt_data(void) {
108 : /* gzip_packed with corrupt compressed data */
109 : TlWriter w;
110 1 : tl_writer_init(&w);
111 1 : tl_write_uint32(&w, 0x3072cfa1); /* CRC_gzip_packed */
112 1 : uint8_t garbage[] = { 0xFF, 0xFE, 0xFD, 0xFC, 0xFB };
113 1 : tl_write_bytes(&w, garbage, sizeof(garbage));
114 :
115 : uint8_t out[1024];
116 1 : size_t out_len = 0;
117 1 : int rc = rpc_unwrap_gzip(w.data, w.len, out, sizeof(out), &out_len);
118 1 : tl_writer_free(&w);
119 :
120 1 : ASSERT(rc == -1, "corrupt gzip data should fail");
121 : }
122 :
123 1 : void test_unwrap_gzip_empty_bytes(void) {
124 : /* gzip_packed with empty bytes field */
125 : TlWriter w;
126 1 : tl_writer_init(&w);
127 1 : tl_write_uint32(&w, 0x3072cfa1);
128 1 : tl_write_bytes(&w, (uint8_t[]){0}, 0); /* empty bytes */
129 :
130 : uint8_t out[1024];
131 1 : size_t out_len = 0;
132 1 : int rc = rpc_unwrap_gzip(w.data, w.len, out, sizeof(out), &out_len);
133 1 : tl_writer_free(&w);
134 :
135 1 : ASSERT(rc == -1, "empty gzip data should fail");
136 : }
137 :
138 1 : void test_tinf_gzip_direct(void) {
139 : /* Verify tinf works directly with known data */
140 : uint8_t out[256];
141 1 : unsigned int out_len = sizeof(out);
142 1 : int rc = tinf_gzip_uncompress(out, &out_len, gzip_hello, (unsigned int)gzip_hello_len);
143 1 : ASSERT(rc == TINF_OK, "tinf_gzip_uncompress should succeed");
144 1 : ASSERT(out_len == 15, "decompressed length should be 15");
145 1 : ASSERT(memcmp(out, "Hello, MTProto!", 15) == 0, "data should match");
146 : }
147 :
148 : /* ---- Test suite entry point ---- */
149 :
150 1 : void test_gzip(void) {
151 1 : RUN_TEST(test_unwrap_gzip_passthrough);
152 1 : RUN_TEST(test_unwrap_gzip_passthrough_constructor);
153 1 : RUN_TEST(test_unwrap_gzip_decompress);
154 1 : RUN_TEST(test_unwrap_gzip_null_args);
155 1 : RUN_TEST(test_unwrap_gzip_short_data);
156 1 : RUN_TEST(test_unwrap_gzip_buffer_too_small);
157 1 : RUN_TEST(test_unwrap_gzip_corrupt_data);
158 1 : RUN_TEST(test_unwrap_gzip_empty_bytes);
159 1 : RUN_TEST(test_tinf_gzip_direct);
160 1 : }
|