1/*
2 * Copyright (c) 2014-2020 Pavel Kalvoda <me@pavelkalvoda.com>
3 *
4 * libcbor is free software; you can redistribute it and/or modify
5 * it under the terms of the MIT license. See LICENSE for details.
6 */
7
8#include <string.h>
9#include "assertions.h"
10#include "cbor.h"
11#include "test_allocator.h"
12
13cbor_item_t *string;
14struct cbor_load_result res;
15
16unsigned char empty_string_data[] = {0x60};
17
18static void test_empty_string(void **_CBOR_UNUSED(_state)) {
19  string = cbor_load(empty_string_data, 1, &res);
20  assert_non_null(string);
21  assert_true(cbor_typeof(string) == CBOR_TYPE_STRING);
22  assert_true(cbor_isa_string(string));
23  assert_size_equal(cbor_string_length(string), 0);
24  assert_size_equal(cbor_string_codepoint_count(string), 0);
25  assert_true(res.read == 1);
26  cbor_decref(&string);
27  assert_null(string);
28}
29
30unsigned char short_string_data[] = {0x6C, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20,
31                                     0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21};
32
33/*                              0x60 + 12 | Hello world! */
34static void test_short_string(void **_CBOR_UNUSED(_state)) {
35  string = cbor_load(short_string_data, 13, &res);
36  assert_non_null(string);
37  assert_true(cbor_typeof(string) == CBOR_TYPE_STRING);
38  assert_true(cbor_isa_string(string));
39  assert_size_equal(cbor_string_length(string), 12);
40  assert_size_equal(cbor_string_codepoint_count(string), 12);
41  assert_memory_equal(&"Hello world!", cbor_string_handle(string), 12);
42  assert_true(res.read == 13);
43  cbor_decref(&string);
44  assert_null(string);
45}
46
47unsigned char short_multibyte_string_data[] = {
48    0x6F, 0xC4, 0x8C, 0x61, 0x75, 0x65, 0x73, 0x20,
49    0xC3, 0x9F, 0x76, 0xC4, 0x9B, 0x74, 0x65, 0x21};
50
51/*                              0x60 + 15 | ��aues ��v��te! */
52static void test_short_multibyte_string(void **_CBOR_UNUSED(_state)) {
53  string = cbor_load(short_multibyte_string_data, 16, &res);
54  assert_non_null(string);
55  assert_true(cbor_typeof(string) == CBOR_TYPE_STRING);
56  assert_true(cbor_isa_string(string));
57  assert_size_equal(cbor_string_length(string), 15);
58  assert_size_equal(cbor_string_codepoint_count(string), 12);
59  assert_memory_equal(&"��aues ��v��te!", cbor_string_handle(string), 15);
60  assert_true(res.read == 16);
61  cbor_decref(&string);
62  assert_null(string);
63}
64
65unsigned char int8_string_data[] = {
66    0x78, 0x96, 0x4C, 0x6F, 0x72, 0x65, 0x6D, 0x20, 0x69, 0x70, 0x73, 0x75,
67    0x6D, 0x20, 0x64, 0x6F, 0x6C, 0x6F, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20,
68    0x61, 0x6D, 0x65, 0x74, 0x2C, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x65, 0x63,
69    0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73,
70    0x63, 0x69, 0x6E, 0x67, 0x20, 0x65, 0x6C, 0x69, 0x74, 0x2E, 0x20, 0x44,
71    0x6F, 0x6E, 0x65, 0x63, 0x20, 0x6D, 0x69, 0x20, 0x74, 0x65, 0x6C, 0x6C,
72    0x75, 0x73, 0x2C, 0x20, 0x69, 0x61, 0x63, 0x75, 0x6C, 0x69, 0x73, 0x20,
73    0x6E, 0x65, 0x63, 0x20, 0x76, 0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6C,
74    0x75, 0x6D, 0x20, 0x71, 0x75, 0x69, 0x73, 0x2C, 0x20, 0x66, 0x65, 0x72,
75    0x6D, 0x65, 0x6E, 0x74, 0x75, 0x6D, 0x20, 0x6E, 0x6F, 0x6E, 0x20, 0x66,
76    0x65, 0x6C, 0x69, 0x73, 0x2E, 0x20, 0x4D, 0x61, 0x65, 0x63, 0x65, 0x6E,
77    0x61, 0x73, 0x20, 0x75, 0x74, 0x20, 0x6A, 0x75, 0x73, 0x74, 0x6F, 0x20,
78    0x70, 0x6F, 0x73, 0x75, 0x65, 0x72, 0x65, 0x2E};
79
80/*                                          150 | Lorem ....*/
81static void test_int8_string(void **_CBOR_UNUSED(_state)) {
82  string = cbor_load(int8_string_data, 152, &res);
83  assert_non_null(string);
84  assert_true(cbor_typeof(string) == CBOR_TYPE_STRING);
85  assert_true(cbor_isa_string(string));
86  assert_size_equal(cbor_string_length(string), 150);
87  assert_size_equal(cbor_string_codepoint_count(string), 150);
88  assert_memory_equal(
89		&"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.",
90		cbor_string_handle(string),
91		150
92	);
93  assert_true(res.read == 152);
94  cbor_decref(&string);
95  assert_null(string);
96}
97
98unsigned char int16_string_data[] = {
99    0x79, 0x00, 0x96, 0x4C, 0x6F, 0x72, 0x65, 0x6D, 0x20, 0x69, 0x70, 0x73,
100    0x75, 0x6D, 0x20, 0x64, 0x6F, 0x6C, 0x6F, 0x72, 0x20, 0x73, 0x69, 0x74,
101    0x20, 0x61, 0x6D, 0x65, 0x74, 0x2C, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x65,
102    0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69,
103    0x73, 0x63, 0x69, 0x6E, 0x67, 0x20, 0x65, 0x6C, 0x69, 0x74, 0x2E, 0x20,
104    0x44, 0x6F, 0x6E, 0x65, 0x63, 0x20, 0x6D, 0x69, 0x20, 0x74, 0x65, 0x6C,
105    0x6C, 0x75, 0x73, 0x2C, 0x20, 0x69, 0x61, 0x63, 0x75, 0x6C, 0x69, 0x73,
106    0x20, 0x6E, 0x65, 0x63, 0x20, 0x76, 0x65, 0x73, 0x74, 0x69, 0x62, 0x75,
107    0x6C, 0x75, 0x6D, 0x20, 0x71, 0x75, 0x69, 0x73, 0x2C, 0x20, 0x66, 0x65,
108    0x72, 0x6D, 0x65, 0x6E, 0x74, 0x75, 0x6D, 0x20, 0x6E, 0x6F, 0x6E, 0x20,
109    0x66, 0x65, 0x6C, 0x69, 0x73, 0x2E, 0x20, 0x4D, 0x61, 0x65, 0x63, 0x65,
110    0x6E, 0x61, 0x73, 0x20, 0x75, 0x74, 0x20, 0x6A, 0x75, 0x73, 0x74, 0x6F,
111    0x20, 0x70, 0x6F, 0x73, 0x75, 0x65, 0x72, 0x65, 0x2E};
112/*                                          150 | Lorem ....*/
113/* This valid but not realistic - length 150 could be encoded in a single
114 * uint8_t (but we need to keep the test files reasonably compact) */
115static void test_int16_string(void **_CBOR_UNUSED(_state)) {
116  string = cbor_load(int16_string_data, 153, &res);
117  assert_non_null(string);
118  assert_true(cbor_typeof(string) == CBOR_TYPE_STRING);
119  assert_true(cbor_isa_string(string));
120  assert_size_equal(cbor_string_length(string), 150);
121  assert_size_equal(cbor_string_codepoint_count(string), 150);
122  assert_memory_equal(
123		&"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.",
124		cbor_string_handle(string),
125		150
126	);
127  assert_true(res.read == 153);
128  cbor_decref(&string);
129  assert_null(string);
130}
131
132unsigned char int32_string_data[] = {
133    0x7A, 0x00, 0x00, 0x00, 0x96, 0x4C, 0x6F, 0x72, 0x65, 0x6D, 0x20, 0x69,
134    0x70, 0x73, 0x75, 0x6D, 0x20, 0x64, 0x6F, 0x6C, 0x6F, 0x72, 0x20, 0x73,
135    0x69, 0x74, 0x20, 0x61, 0x6D, 0x65, 0x74, 0x2C, 0x20, 0x63, 0x6F, 0x6E,
136    0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69,
137    0x70, 0x69, 0x73, 0x63, 0x69, 0x6E, 0x67, 0x20, 0x65, 0x6C, 0x69, 0x74,
138    0x2E, 0x20, 0x44, 0x6F, 0x6E, 0x65, 0x63, 0x20, 0x6D, 0x69, 0x20, 0x74,
139    0x65, 0x6C, 0x6C, 0x75, 0x73, 0x2C, 0x20, 0x69, 0x61, 0x63, 0x75, 0x6C,
140    0x69, 0x73, 0x20, 0x6E, 0x65, 0x63, 0x20, 0x76, 0x65, 0x73, 0x74, 0x69,
141    0x62, 0x75, 0x6C, 0x75, 0x6D, 0x20, 0x71, 0x75, 0x69, 0x73, 0x2C, 0x20,
142    0x66, 0x65, 0x72, 0x6D, 0x65, 0x6E, 0x74, 0x75, 0x6D, 0x20, 0x6E, 0x6F,
143    0x6E, 0x20, 0x66, 0x65, 0x6C, 0x69, 0x73, 0x2E, 0x20, 0x4D, 0x61, 0x65,
144    0x63, 0x65, 0x6E, 0x61, 0x73, 0x20, 0x75, 0x74, 0x20, 0x6A, 0x75, 0x73,
145    0x74, 0x6F, 0x20, 0x70, 0x6F, 0x73, 0x75, 0x65, 0x72, 0x65, 0x2E};
146
147/*                                          150 | Lorem ....*/
148static void test_int32_string(void **_CBOR_UNUSED(_state)) {
149  string = cbor_load(int32_string_data, 155, &res);
150  assert_non_null(string);
151  assert_true(cbor_typeof(string) == CBOR_TYPE_STRING);
152  assert_true(cbor_isa_string(string));
153  assert_size_equal(cbor_string_length(string), 150);
154  assert_size_equal(cbor_string_codepoint_count(string), 150);
155  assert_memory_equal(
156		&"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.",
157		cbor_string_handle(string),
158		150
159	);
160  assert_true(res.read == 155);
161  cbor_decref(&string);
162  assert_null(string);
163}
164
165unsigned char int64_string_data[] = {
166    0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x4C, 0x6F, 0x72,
167    0x65, 0x6D, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6D, 0x20, 0x64, 0x6F, 0x6C,
168    0x6F, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6D, 0x65, 0x74, 0x2C,
169    0x20, 0x63, 0x6F, 0x6E, 0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72,
170    0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, 0x69, 0x6E, 0x67, 0x20,
171    0x65, 0x6C, 0x69, 0x74, 0x2E, 0x20, 0x44, 0x6F, 0x6E, 0x65, 0x63, 0x20,
172    0x6D, 0x69, 0x20, 0x74, 0x65, 0x6C, 0x6C, 0x75, 0x73, 0x2C, 0x20, 0x69,
173    0x61, 0x63, 0x75, 0x6C, 0x69, 0x73, 0x20, 0x6E, 0x65, 0x63, 0x20, 0x76,
174    0x65, 0x73, 0x74, 0x69, 0x62, 0x75, 0x6C, 0x75, 0x6D, 0x20, 0x71, 0x75,
175    0x69, 0x73, 0x2C, 0x20, 0x66, 0x65, 0x72, 0x6D, 0x65, 0x6E, 0x74, 0x75,
176    0x6D, 0x20, 0x6E, 0x6F, 0x6E, 0x20, 0x66, 0x65, 0x6C, 0x69, 0x73, 0x2E,
177    0x20, 0x4D, 0x61, 0x65, 0x63, 0x65, 0x6E, 0x61, 0x73, 0x20, 0x75, 0x74,
178    0x20, 0x6A, 0x75, 0x73, 0x74, 0x6F, 0x20, 0x70, 0x6F, 0x73, 0x75, 0x65,
179    0x72, 0x65, 0x2E};
180
181/*                                          150 | Lorem ....*/
182static void test_int64_string(void **_CBOR_UNUSED(_state)) {
183  string = cbor_load(int64_string_data, 159, &res);
184  assert_non_null(string);
185  assert_true(cbor_typeof(string) == CBOR_TYPE_STRING);
186  assert_true(cbor_isa_string(string));
187  assert_size_equal(cbor_string_length(string), 150);
188  assert_size_equal(cbor_string_codepoint_count(string), 150);
189  assert_memory_equal(
190		&"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi tellus, iaculis nec vestibulum quis, fermentum non felis. Maecenas ut justo posuere.",
191		cbor_string_handle(string),
192		150
193	);
194  assert_true(res.read == 159);
195  cbor_decref(&string);
196  assert_null(string);
197}
198
199unsigned char short_indef_string_data[] = {0x7F, 0x78, 0x01, 0x65, 0xFF, 0xFF};
200
201/*                                         start |   string      | break| extra
202 */
203
204static void test_short_indef_string(void **_CBOR_UNUSED(_state)) {
205  string = cbor_load(short_indef_string_data, 6, &res);
206  assert_non_null(string);
207  assert_true(cbor_typeof(string) == CBOR_TYPE_STRING);
208  assert_true(cbor_isa_string(string));
209  assert_true(cbor_string_length(string) == 0);
210  assert_true(cbor_string_is_indefinite(string));
211  assert_true(cbor_string_chunk_count(string) == 1);
212  assert_true(res.read == 5);
213  assert_true(cbor_isa_string(cbor_string_chunks_handle(string)[0]));
214  assert_true(cbor_string_length(cbor_string_chunks_handle(string)[0]) == 1);
215  assert_true(*cbor_string_handle(cbor_string_chunks_handle(string)[0]) == 'e');
216  cbor_decref(&string);
217  assert_null(string);
218}
219
220static void test_invalid_utf(void **_CBOR_UNUSED(_state)) {
221  /* 0x60 + 1 | 0xC5 (invalid unfinished 2B codepoint) */
222  unsigned char string_data[] = {0x61, 0xC5};
223  string = cbor_load(string_data, 2, &res);
224
225  assert_non_null(string);
226  assert_true(cbor_typeof(string) == CBOR_TYPE_STRING);
227  assert_true(cbor_isa_string(string));
228  assert_size_equal(cbor_string_length(string), 1);
229  assert_size_equal(cbor_string_codepoint_count(string), 0);
230  assert_true(cbor_string_is_definite(string));
231  assert_true(res.read == 2);
232
233  cbor_decref(&string);
234}
235
236static void test_inline_creation(void **_CBOR_UNUSED(_state)) {
237  string = cbor_build_string("Hello!");
238  assert_memory_equal(cbor_string_handle(string), "Hello!", strlen("Hello!"));
239  cbor_decref(&string);
240}
241
242static void test_string_creation(void **_CBOR_UNUSED(_state)) {
243  WITH_FAILING_MALLOC({ assert_null(cbor_new_definite_string()); });
244
245  WITH_FAILING_MALLOC({ assert_null(cbor_new_indefinite_string()); });
246  WITH_MOCK_MALLOC({ assert_null(cbor_new_indefinite_string()); }, 2, MALLOC,
247                   MALLOC_FAIL);
248
249  WITH_FAILING_MALLOC({ assert_null(cbor_build_string("Test")); });
250  WITH_MOCK_MALLOC({ assert_null(cbor_build_string("Test")); }, 2, MALLOC,
251                   MALLOC_FAIL);
252
253  WITH_FAILING_MALLOC({ assert_null(cbor_build_stringn("Test", 4)); });
254  WITH_MOCK_MALLOC({ assert_null(cbor_build_stringn("Test", 4)); }, 2, MALLOC,
255                   MALLOC_FAIL);
256}
257
258static void test_string_add_chunk(void **_CBOR_UNUSED(_state)) {
259  WITH_MOCK_MALLOC(
260      {
261        cbor_item_t *string = cbor_new_indefinite_string();
262        cbor_item_t *chunk = cbor_build_string("Hello!");
263
264        assert_false(cbor_string_add_chunk(string, chunk));
265        assert_size_equal(cbor_string_chunk_count(string), 0);
266        assert_size_equal(((struct cbor_indefinite_string_data *)string->data)
267                              ->chunk_capacity,
268                          0);
269
270        cbor_decref(&chunk);
271        cbor_decref(&string);
272      },
273      5, MALLOC, MALLOC, MALLOC, MALLOC, REALLOC_FAIL);
274}
275
276static void test_add_chunk_reallocation_overflow(void **_CBOR_UNUSED(_state)) {
277  string = cbor_new_indefinite_string();
278  cbor_item_t *chunk = cbor_build_string("Hello!");
279  struct cbor_indefinite_string_data *metadata =
280      (struct cbor_indefinite_string_data *)string->data;
281  // Pretend we already have many chunks allocated
282  metadata->chunk_count = SIZE_MAX;
283  metadata->chunk_capacity = SIZE_MAX;
284
285  assert_false(cbor_string_add_chunk(string, chunk));
286  assert_size_equal(cbor_refcount(chunk), 1);
287
288  metadata->chunk_count = 0;
289  metadata->chunk_capacity = 0;
290  cbor_decref(&chunk);
291  cbor_decref(&string);
292}
293
294static void test_set_handle(void **_CBOR_UNUSED(_state)) {
295  string = cbor_new_definite_string();
296  char *test_string = "Hello";
297  unsigned char *string_data = malloc(strlen(test_string));
298  memcpy(string_data, test_string, strlen(test_string));
299  assert_ptr_not_equal(string_data, NULL);
300  cbor_string_set_handle(string, string_data, strlen(test_string));
301
302  assert_ptr_equal(cbor_string_handle(string), string_data);
303  assert_size_equal(cbor_string_length(string), 5);
304  assert_size_equal(cbor_string_codepoint_count(string), 5);
305
306  cbor_decref(&string);
307}
308
309static void test_set_handle_multibyte_codepoint(void **_CBOR_UNUSED(_state)) {
310  string = cbor_new_definite_string();
311  // "��t��st����ko" in UTF-8
312  char *test_string = "\xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko";
313  unsigned char *string_data = malloc(strlen(test_string));
314  memcpy(string_data, test_string, strlen(test_string));
315  assert_ptr_not_equal(string_data, NULL);
316  cbor_string_set_handle(string, string_data, strlen(test_string));
317
318  assert_ptr_equal(cbor_string_handle(string), string_data);
319  assert_size_equal(cbor_string_length(string), 13);
320  assert_size_equal(cbor_string_codepoint_count(string), 9);
321
322  cbor_decref(&string);
323}
324
325static void test_set_handle_invalid_utf(void **_CBOR_UNUSED(_state)) {
326  string = cbor_new_definite_string();
327  // Invalid multi-byte character (missing the second byte).
328  char *test_string = "Test: \xc5";
329  unsigned char *string_data = malloc(strlen(test_string));
330  memcpy(string_data, test_string, strlen(test_string));
331  assert_ptr_not_equal(string_data, NULL);
332  cbor_string_set_handle(string, string_data, strlen(test_string));
333
334  assert_ptr_equal(cbor_string_handle(string), string_data);
335  assert_size_equal(cbor_string_length(string), 7);
336  assert_size_equal(cbor_string_codepoint_count(string), 0);
337
338  cbor_decref(&string);
339}
340
341int main(void) {
342  const struct CMUnitTest tests[] = {
343      cmocka_unit_test(test_empty_string),
344      cmocka_unit_test(test_short_string),
345      cmocka_unit_test(test_short_multibyte_string),
346      cmocka_unit_test(test_int8_string),
347      cmocka_unit_test(test_int16_string),
348      cmocka_unit_test(test_int32_string),
349      cmocka_unit_test(test_int64_string),
350      cmocka_unit_test(test_short_indef_string),
351      cmocka_unit_test(test_invalid_utf),
352      cmocka_unit_test(test_inline_creation),
353      cmocka_unit_test(test_string_creation),
354      cmocka_unit_test(test_string_add_chunk),
355      cmocka_unit_test(test_add_chunk_reallocation_overflow),
356      cmocka_unit_test(test_set_handle),
357      cmocka_unit_test(test_set_handle_multibyte_codepoint),
358      cmocka_unit_test(test_set_handle_invalid_utf),
359  };
360  return cmocka_run_group_tests(tests, NULL, NULL);
361}
362