/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
#include "test-lib.h"
#include "str.h"
#include "istream-private.h"
#include "istream-jsonstr.h"
static const struct {
const char *input;
const char *output;
int stream_errno;
} tests[] = {
{ "foo\\\\\\\"\\b\\f\\n\\r\\t\\u0001\\uffff\"",
"foo\\\"\b\f\n\r\t\001\xEF\xBF\xBF", 0 },
{ "\\ud801\\udc37\"", "\xf0\x90\x90\xb7", 0 }, /* valid codepoint */
{ "\"", "", 0 },
{ "foo\\?\"", "foo", EINVAL },
{ "foo\\?\"", "foo", EINVAL },
{ "", "", EPIPE },
{ "\\\"", "\"", EPIPE },
{ "foo", "foo", EPIPE },
{ "\\ud801", "", EPIPE }, /* high surrogate alone */
{ "\\udced\\udc37\"", "", EINVAL }, /* low surrogate before high */
{ "\\ud8011\\udc37\"", "", EINVAL }, /* has extra 1 in middle */
{ "hello \\udc37\"", "hello ", EINVAL }, /* low surrogate before high with valid prefix*/
{ "hello \\ud801", "hello ", EPIPE }, /* high surrogate alone with valid prefix */
{ "\\uabcg", "", EINVAL }, /* invalid hex value */
};
static void
run_test_buffer(const char *json_input, const char *output, int stream_errno,
unsigned int skip_count)
{
size_t json_input_len = strlen(json_input);
struct istream *input_data, *input;
const unsigned char *data;
size_t i, size;
ssize_t ret = 0;
input_data = test_istream_create_data(json_input, json_input_len);
test_istream_set_allow_eof(input_data, FALSE);
input = i_stream_create_jsonstr(input_data);
for (i = 1; i < json_input_len;) {
test_istream_set_size(input_data, i);
while ((ret = i_stream_read(input)) > 0) ;
if (ret == -1 && stream_errno != 0)
break;
test_assert_idx(ret == 0, i);
if (i + skip_count < json_input_len)
i += skip_count;
else
i++;
}
test_istream_set_allow_eof(input_data, TRUE);
test_istream_set_size(input_data, json_input_len);
ret = i_stream_read(input);
while (ret > 0 && stream_errno != 0)
ret = i_stream_read(input);
test_assert(ret == -1);
test_assert(input->stream_errno == stream_errno);
if (stream_errno == 0) {
data = i_stream_get_data(input, &size);
test_assert(size == strlen(output));
if (size > 0)
test_assert(memcmp(data, output, size) == 0);
}
i_stream_unref(&input);
i_stream_unref(&input_data);
}
static void
run_test(const char *json_input, const char *output, int stream_errno)
{
for (unsigned int i = 1; i <= 5; i++)
run_test_buffer(json_input, output, stream_errno, i);
}
static void test_istream_jsonstr_autoretry(void)
{
const char *json_input = "\\u0001\"";
const size_t json_input_len = strlen(json_input);
struct istream *input_data, *input;
test_begin("istream-jsonstr autoretry");
input_data = test_istream_create_data(json_input, json_input_len);
input = i_stream_create_jsonstr(input_data);
test_istream_set_size(input_data, 2);
test_assert(i_stream_read(input_data) == 2);
test_istream_set_size(input_data, json_input_len);
test_assert(i_stream_read(input) == 1);
test_assert(i_stream_read(input) == -1);
i_stream_unref(&input);
i_stream_unref(&input_data);
test_end();
}
static void test_istream_jsonstr_partial(void)
{
size_t len = 0;
const char *json_input = "hello\\u0060x\"";
const char *output = "hello`x";
const size_t json_input_len = strlen(json_input);
struct istream *input_data, *input;
test_begin("istream-jsonstr partial");
input_data = test_istream_create_data(json_input, json_input_len);
input = i_stream_create_jsonstr(input_data);
test_istream_set_size(input_data, 9);
test_assert(i_stream_read(input) == 5);
test_istream_set_size(input_data, json_input_len);
test_assert(i_stream_read(input) == 2);
test_assert(i_stream_read(input) == -1);
test_assert(memcmp(i_stream_get_data(input, &len), output, I_MIN(len, strlen(output))) == 0 &&
len == strlen(output));
i_stream_unref(&input);
i_stream_unref(&input_data);
test_end();
}
void test_istream_jsonstr(void)
{
unsigned int i;
for (i = 0; i < N_ELEMENTS(tests); i++) {
test_begin(t_strdup_printf("istream-jsonstr %u", i+1));
run_test(tests[i].input, tests[i].output, tests[i].stream_errno);
test_end();
}
test_istream_jsonstr_autoretry();
test_istream_jsonstr_partial();
}