1/* Make sure that all fields are freed in various scenarios. */
2
3#include <pb_decode.h>
4#include <pb_encode.h>
5#include <malloc_wrappers.h>
6#include <stdio.h>
7#include <test_helpers.h>
8#include "mem_release.pb.h"
9
10#define TEST(x) if (!(x)) { \
11    fprintf(stderr, "Test " #x " on line %d failed.\n", __LINE__); \
12    return false; \
13    }
14
15static char *test_str_arr[] = {"1", "2", ""};
16static SubMessage test_msg_arr[] = {SubMessage_init_zero, SubMessage_init_zero};
17static pb_extension_t ext1, ext2;
18
19static void fill_TestMessage(TestMessage *msg)
20{
21    msg->static_req_submsg.dynamic_str = "12345";
22    msg->static_req_submsg.dynamic_str_arr_count = 3;
23    msg->static_req_submsg.dynamic_str_arr = test_str_arr;
24    msg->static_req_submsg.dynamic_submsg_count = 2;
25    msg->static_req_submsg.dynamic_submsg = test_msg_arr;
26    msg->static_req_submsg.dynamic_submsg[1].dynamic_str = "abc";
27    msg->static_opt_submsg.dynamic_str = "abc";
28    msg->static_rep_submsg_count = 2;
29    msg->static_rep_submsg[1].dynamic_str = "abc";
30    msg->has_static_opt_submsg = true;
31    msg->dynamic_submsg = &msg->static_req_submsg;
32
33    msg->extensions = &ext1;
34    ext1.type = &dynamic_ext;
35    ext1.dest = &msg->static_req_submsg;
36    ext1.next = &ext2;
37    ext2.type = &static_ext;
38    ext2.dest = &msg->static_req_submsg;
39    ext2.next = NULL;
40}
41
42/* Basic fields, nested submessages, extensions */
43static bool test_TestMessage()
44{
45    uint8_t buffer[256];
46    size_t msgsize;
47
48    /* Construct a message with various fields filled in */
49    {
50        TestMessage msg = TestMessage_init_zero;
51        pb_ostream_t stream;
52
53        fill_TestMessage(&msg);
54
55        stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
56        if (!pb_encode(&stream, TestMessage_fields, &msg))
57        {
58            fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream));
59            return false;
60        }
61        msgsize = stream.bytes_written;
62    }
63
64    /* Output encoded message for debug */
65    SET_BINARY_MODE(stdout);
66    fwrite(buffer, 1, msgsize, stdout);
67
68    /* Decode memory using dynamic allocation */
69    {
70        TestMessage msg = TestMessage_init_zero;
71        pb_istream_t stream;
72        SubMessage ext2_dest;
73
74        msg.extensions = &ext1;
75        ext1.type = &dynamic_ext;
76        ext1.dest = NULL;
77        ext1.next = &ext2;
78        ext2.type = &static_ext;
79        ext2.dest = &ext2_dest;
80        ext2.next = NULL;
81
82        stream = pb_istream_from_buffer(buffer, msgsize);
83        if (!pb_decode(&stream, TestMessage_fields, &msg))
84        {
85            fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&stream));
86            return false;
87        }
88
89        /* Make sure it encodes back to same data */
90        {
91            uint8_t buffer2[256];
92            pb_ostream_t ostream = pb_ostream_from_buffer(buffer2, sizeof(buffer2));
93            TEST(pb_encode(&ostream, TestMessage_fields, &msg));
94            TEST(ostream.bytes_written == msgsize);
95            TEST(memcmp(buffer, buffer2, msgsize) == 0);
96        }
97
98        /* Make sure that malloc counters work */
99        TEST(get_alloc_count() > 0);
100
101        /* Make sure that pb_release releases everything */
102        pb_release(TestMessage_fields, &msg);
103        TEST(get_alloc_count() == 0);
104
105        /* Check that double-free is a no-op */
106        pb_release(TestMessage_fields, &msg);
107        TEST(get_alloc_count() == 0);
108    }
109
110    return true;
111}
112
113/* Oneofs */
114static bool test_OneofMessage()
115{
116    uint8_t buffer[256];
117    size_t msgsize;
118
119    {
120        pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
121
122        /* Encode first with TestMessage */
123        {
124            OneofMessage msg = OneofMessage_init_zero;
125            msg.which_msgs = OneofMessage_msg1_tag;
126
127            fill_TestMessage(&msg.msgs.msg1);
128
129            if (!pb_encode(&stream, OneofMessage_fields, &msg))
130            {
131                fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream));
132                return false;
133            }
134        }
135
136        /* Encode second with SubMessage, invoking 'merge' behaviour */
137        {
138            OneofMessage msg = OneofMessage_init_zero;
139            msg.which_msgs = OneofMessage_msg2_tag;
140
141            msg.first = 999;
142            msg.msgs.msg2.dynamic_str = "ABCD";
143            msg.last = 888;
144
145            if (!pb_encode(&stream, OneofMessage_fields, &msg))
146            {
147                fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream));
148                return false;
149            }
150        }
151        msgsize = stream.bytes_written;
152    }
153
154    {
155        OneofMessage msg = OneofMessage_init_zero;
156        pb_istream_t stream = pb_istream_from_buffer(buffer, msgsize);
157        if (!pb_decode(&stream, OneofMessage_fields, &msg))
158        {
159            fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&stream));
160            return false;
161        }
162
163        TEST(msg.first == 999);
164        TEST(msg.which_msgs == OneofMessage_msg2_tag);
165        TEST(msg.msgs.msg2.dynamic_str);
166        TEST(strcmp(msg.msgs.msg2.dynamic_str, "ABCD") == 0);
167        TEST(msg.msgs.msg2.dynamic_str_arr == NULL);
168        TEST(msg.msgs.msg2.dynamic_submsg == NULL);
169        TEST(msg.last == 888);
170
171        pb_release(OneofMessage_fields, &msg);
172        TEST(get_alloc_count() == 0);
173        pb_release(OneofMessage_fields, &msg);
174        TEST(get_alloc_count() == 0);
175    }
176
177    return true;
178}
179
180static bool dummy_decode_cb(pb_istream_t *stream, const pb_field_t *field, void **arg)
181{
182    return false;
183}
184
185/* Garbage input */
186static bool test_Garbage()
187{
188    const uint8_t buffer[] = "I'm only happy when it rains";
189    const size_t msgsize = sizeof(buffer);
190
191    {
192        OneofMessage msg = OneofMessage_init_zero;
193        pb_istream_t stream = pb_istream_from_buffer(buffer, msgsize);
194        TEST(!pb_decode(&stream, OneofMessage_fields, &msg));
195    }
196
197    {
198        TestMessage msg = TestMessage_init_zero;
199        pb_istream_t stream = pb_istream_from_buffer(buffer, msgsize);
200        TEST(!pb_decode(&stream, TestMessage_fields, &msg));
201    }
202
203    {
204        RepeatedMessage msg = RepeatedMessage_init_zero;
205        pb_istream_t stream = pb_istream_from_buffer(buffer, msgsize);
206        msg.subs.arg = NULL;
207        msg.subs.funcs.decode = dummy_decode_cb;
208        TEST(!pb_decode(&stream, RepeatedMessage_fields, &msg));
209    }
210
211    return true;
212}
213
214int main()
215{
216    if (test_TestMessage() && test_OneofMessage() && test_Garbage())
217        return 0;
218    else
219        return 1;
220}
221
222