From c80324a37c86188f4339820eaccc50c6d02b5b88 Mon Sep 17 00:00:00 2001 From: Matt Bennett Date: Mon, 14 Oct 2019 09:40:34 +1300 Subject: [PATCH 1/2] Output strings for int64 and uint64 in json The proto3 json mapping specifies that 64 bit values will be written out as strings. One reason for this is to prevent overflowing javascript representation of numbers, which is limited to approximately 2^53. --- src/protobuf2json.c | 9 +++++++-- test/test-reversible.c | 21 +++++++++++---------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/protobuf2json.c b/src/protobuf2json.c index ed05da9..1baef3d 100644 --- a/src/protobuf2json.c +++ b/src/protobuf2json.c @@ -10,6 +10,7 @@ #include #include #include +#include /* Interface definitions */ #include "protobuf2json.h" @@ -84,6 +85,8 @@ static int protobuf2json_process_field( char *error_string, size_t error_size ) { + char buf[32]; + switch (field_descriptor->type) { case PROTOBUF_C_TYPE_INT32: case PROTOBUF_C_TYPE_SINT32: @@ -97,11 +100,13 @@ static int protobuf2json_process_field( case PROTOBUF_C_TYPE_INT64: case PROTOBUF_C_TYPE_SINT64: case PROTOBUF_C_TYPE_SFIXED64: - *json_value = json_integer(*(int64_t *)protobuf_value); + sprintf (buf, "%" PRId64, *(int64_t *)protobuf_value); + *json_value = json_string(buf); break; case PROTOBUF_C_TYPE_UINT64: case PROTOBUF_C_TYPE_FIXED64: - *json_value = json_integer(*(uint64_t *)protobuf_value); + sprintf (buf, "%" PRIu64, *(uint64_t *)protobuf_value); + *json_value = json_string(buf); break; case PROTOBUF_C_TYPE_FLOAT: *json_value = json_real(*(float *)protobuf_value); diff --git a/test/test-reversible.c b/test/test-reversible.c index 8c043a7..6ea0400 100644 --- a/test/test-reversible.c +++ b/test/test-reversible.c @@ -256,27 +256,28 @@ TEST_IMPL(reversible__numbers) { " 0\n" " ],\n" + /* 64 bit values are always written as strings. */ " \"value_int64\": [\n" - " 9223372036854775807,\n" - " 0\n" + " \"9223372036854775807\",\n" + " \"0\"\n" " ],\n" " \"value_sint64\": [\n" - " -9223372036854775808,\n" - " 0\n" + " \"-9223372036854775808\",\n" + " \"0\"\n" " ],\n" " \"value_sfixed64\": [\n" - " -9223372036854775808,\n" - " 0\n" + " \"-9223372036854775808\",\n" + " \"0\"\n" " ],\n" /* JSON does not support max(unsigned long long) */ " \"value_uint64\": [\n" - " 9223372036854775807,\n" - " 0\n" + " \"9223372036854775807\",\n" + " \"0\"\n" " ],\n" " \"value_fixed64\": [\n" - " 9223372036854775807,\n" - " 0\n" + " \"9223372036854775807\",\n" + " \"0\"\n" " ],\n" " \"value_float\": [\n" From 5d9ddb859652bb0761357e1f7a4daaa955879501 Mon Sep 17 00:00:00 2001 From: Matt Bennett Date: Mon, 14 Oct 2019 09:55:43 +1300 Subject: [PATCH 2/2] Enhance json2protobuf handling of integer values The proto3 specification JSON mapping specifies that integers should be accepted as strings or number values. This patch adds support for this. It also checks whether the value is within the range of the field that it is going into. This prevents wrapping due to typecasting. 64 bit integers are accepted as numbers, but this is only supported up to a maximum of INT64_MAX, (jansson will reject values higher than this), so it is advisable to use strings. --- src/protobuf2json.c | 225 ++++++++++++++--- test/test-json2protobuf-string.c | 409 ++++++++++++++++++++++++++++++- test/test-list.h | 118 +++++++-- 3 files changed, 682 insertions(+), 70 deletions(-) diff --git a/src/protobuf2json.c b/src/protobuf2json.c index 1baef3d..69a8fd2 100644 --- a/src/protobuf2json.c +++ b/src/protobuf2json.c @@ -10,6 +10,7 @@ #include #include #include +#include #include /* Interface definitions */ @@ -454,71 +455,221 @@ static const char* json2protobuf_integer_name_by_c_type(ProtobufCType type) { } } -static int json2protobuf_process_field( +static bool json2protobuf_integer_within_range ( + ProtobufCType type, + long long value +) { + switch (type) { + case PROTOBUF_C_TYPE_INT32: + case PROTOBUF_C_TYPE_SINT32: + case PROTOBUF_C_TYPE_SFIXED32: + if (value < INT32_MIN || value > INT32_MAX) + { + return false; + } + break; + case PROTOBUF_C_TYPE_UINT32: + case PROTOBUF_C_TYPE_FIXED32: + if (value < 0 || value > UINT32_MAX) + { + return false; + } + break; + case PROTOBUF_C_TYPE_INT64: + case PROTOBUF_C_TYPE_SINT64: + case PROTOBUF_C_TYPE_SFIXED64: + if (value < INT64_MIN || value > INT64_MAX) + { + return false; + } + break; + case PROTOBUF_C_TYPE_UINT64: + case PROTOBUF_C_TYPE_FIXED64: + if (value < 0) + { + return false; + } + break; + default: + return false; + } + return true; +} + +static int json2protobuf_process_uint64_field ( const ProtobufCFieldDescriptor *field_descriptor, json_t *json_value, void *protobuf_value, char *error_string, size_t error_size ) { - if (field_descriptor->type == PROTOBUF_C_TYPE_INT32 - || field_descriptor->type == PROTOBUF_C_TYPE_SINT32 - || field_descriptor->type == PROTOBUF_C_TYPE_SFIXED32 - ) { - if (!json_is_integer(json_value)) { + uint64_t value_uint64_t = 0; + long long llvalue = 0; + char *endp = NULL; + + // if entered as a number, maximum value allowed through by jansson will be LLONG_MAX + // which is too small for full uint64_t, but will fit. + if (json_is_integer(json_value)) + { + value_uint64_t = (uint64_t) json_integer_value (json_value); + // Used to check sign + llvalue = json_integer_value (json_value); + } + // proto 3 spec states that integers may be entered as strings or numbers + else if (json_is_string(json_value)) + { + errno = 0; + value_uint64_t = strtoull (json_string_value (json_value), &endp, 10); + + if (errno == ERANGE || *endp != '\0') + { SET_ERROR_STRING_AND_RETURN( PROTOBUF2JSON_ERR_IS_NOT_INTEGER, - "JSON value is not an integer required for GPB %s", - json2protobuf_integer_name_by_c_type(field_descriptor->type) + "JSON string not successfully converted to value for GPB %s %s", + json2protobuf_integer_name_by_c_type(field_descriptor->type), + field_descriptor->name ); } + // Used to check sign + llvalue = strtoll (json_string_value (json_value), &endp, 10); + } + else + { + SET_ERROR_STRING_AND_RETURN( + PROTOBUF2JSON_ERR_IS_NOT_INTEGER, + "JSON value is not an integer or string required for GPB %s %s", + json2protobuf_integer_name_by_c_type(field_descriptor->type), + field_descriptor->name + ); + } + + if (!json2protobuf_integer_within_range (field_descriptor->type, llvalue)) + { + /* Number/string received was negative */ + SET_ERROR_STRING_AND_RETURN( + PROTOBUF2JSON_ERR_IS_NOT_INTEGER, + "JSON value is not within range for GPB %s %s", + json2protobuf_integer_name_by_c_type(field_descriptor->type), + field_descriptor->name + ); + } - int32_t value_int32_t = (int32_t)json_integer_value(json_value); + memcpy(protobuf_value, &value_uint64_t, sizeof(value_uint64_t)); - memcpy(protobuf_value, &value_int32_t, sizeof(value_int32_t)); - } else if (field_descriptor->type == PROTOBUF_C_TYPE_UINT32 - || field_descriptor->type == PROTOBUF_C_TYPE_FIXED32 - ) { - if (!json_is_integer(json_value)) { + return 0; +} + +static int json2protobuf_process_integer_field ( + const ProtobufCFieldDescriptor *field_descriptor, + json_t *json_value, + void *protobuf_value, + char *error_string, + size_t error_size +) { + int64_t llvalue = 0; + char *endp = NULL; + + // Jansson can pass values up to LLONG_MAX + if (json_is_integer(json_value)) + { + llvalue = (int64_t) json_integer_value (json_value); + } + // proto 3 spec states that integers may be entered as strings or numbers + else if (json_is_string(json_value)) + { + errno = 0; + llvalue = strtoll (json_string_value (json_value), &endp, 10);; + + if (errno == ERANGE || *endp != '\0') + { SET_ERROR_STRING_AND_RETURN( PROTOBUF2JSON_ERR_IS_NOT_INTEGER, - "JSON value is not an integer required for GPB %s", - json2protobuf_integer_name_by_c_type(field_descriptor->type) + "JSON string not successfully converted to value for GPB %s %s", + json2protobuf_integer_name_by_c_type(field_descriptor->type), + field_descriptor->name ); } + } + else + { + SET_ERROR_STRING_AND_RETURN( + PROTOBUF2JSON_ERR_IS_NOT_INTEGER, + "JSON value is not an integer or string required for GPB %s %s", + json2protobuf_integer_name_by_c_type(field_descriptor->type), + field_descriptor->name + ); + } + + if (!json2protobuf_integer_within_range (field_descriptor->type, llvalue)) + { + SET_ERROR_STRING_AND_RETURN( + PROTOBUF2JSON_ERR_IS_NOT_INTEGER, + "JSON value is not within range for GPB %s %s", + json2protobuf_integer_name_by_c_type(field_descriptor->type), + field_descriptor->name + ); + } - uint32_t value_uint32_t = (uint32_t)json_integer_value(json_value); + if (field_descriptor->type == PROTOBUF_C_TYPE_INT32 + || field_descriptor->type == PROTOBUF_C_TYPE_SINT32 + || field_descriptor->type == PROTOBUF_C_TYPE_SFIXED32 + ) { + int32_t value_int32_t = (int32_t) llvalue; + + memcpy(protobuf_value, &value_int32_t, sizeof(value_int32_t)); + } else if (field_descriptor->type == PROTOBUF_C_TYPE_UINT32 + || field_descriptor->type == PROTOBUF_C_TYPE_FIXED32 + ) { + uint32_t value_uint32_t = (uint32_t) llvalue; - memcpy(protobuf_value, &value_uint32_t, sizeof(value_uint32_t)); + memcpy(protobuf_value, &value_uint32_t, sizeof(value_uint32_t)); } else if (field_descriptor->type == PROTOBUF_C_TYPE_INT64 || field_descriptor->type == PROTOBUF_C_TYPE_SINT64 || field_descriptor->type == PROTOBUF_C_TYPE_SFIXED64 ) { - if (!json_is_integer(json_value)) { - SET_ERROR_STRING_AND_RETURN( - PROTOBUF2JSON_ERR_IS_NOT_INTEGER, - "JSON value is not an integer required for GPB %s", - json2protobuf_integer_name_by_c_type(field_descriptor->type) - ); - } + int64_t value_int64_t = (int64_t) llvalue; - int64_t value_int64_t = (int64_t)json_integer_value(json_value); + memcpy(protobuf_value, &value_int64_t, sizeof(value_int64_t)); + } else { + assert(0); + } + + return 0; +} - memcpy(protobuf_value, &value_int64_t, sizeof(value_int64_t)); +static int json2protobuf_process_field( + const ProtobufCFieldDescriptor *field_descriptor, + json_t *json_value, + void *protobuf_value, + char *error_string, + size_t error_size +) { + if (field_descriptor->type == PROTOBUF_C_TYPE_INT32 + || field_descriptor->type == PROTOBUF_C_TYPE_SINT32 + || field_descriptor->type == PROTOBUF_C_TYPE_SFIXED32 + || field_descriptor->type == PROTOBUF_C_TYPE_UINT32 + || field_descriptor->type == PROTOBUF_C_TYPE_FIXED32 + || field_descriptor->type == PROTOBUF_C_TYPE_INT64 + || field_descriptor->type == PROTOBUF_C_TYPE_SINT64 + || field_descriptor->type == PROTOBUF_C_TYPE_SFIXED64 + ) { + int result = json2protobuf_process_integer_field (field_descriptor, json_value, + protobuf_value, error_string, + error_size); + if (result) + { + return result; + } } else if (field_descriptor->type == PROTOBUF_C_TYPE_UINT64 || field_descriptor->type == PROTOBUF_C_TYPE_FIXED64 ) { - if (!json_is_integer(json_value)) { - SET_ERROR_STRING_AND_RETURN( - PROTOBUF2JSON_ERR_IS_NOT_INTEGER, - "JSON value is not an integer required for GPB %s", - json2protobuf_integer_name_by_c_type(field_descriptor->type) - ); + int result = json2protobuf_process_uint64_field (field_descriptor, json_value, + protobuf_value, error_string, + error_size); + if (result) + { + return result; } - - uint64_t value_uint64_t = (uint64_t)json_integer_value(json_value); - - memcpy(protobuf_value, &value_uint64_t, sizeof(value_uint64_t)); } else if (field_descriptor->type == PROTOBUF_C_TYPE_FLOAT) { float value_float; diff --git a/test/test-json2protobuf-string.c b/test/test-json2protobuf-string.c index 12fa950..40e20a6 100644 --- a/test/test-json2protobuf-string.c +++ b/test/test-json2protobuf-string.c @@ -11,6 +11,7 @@ #include "protobuf2json.h" #include +#include TEST_IMPL(json2protobuf_string__error_cannot_parse_wrong_string) { int result; @@ -212,8 +213,50 @@ TEST_IMPL(json2protobuf_string__error_is_not_object_required_for_message) { RETURN_OK(); } -#define TEST_IMPL_IS_NOT_INTEGER(type) \ - TEST_IMPL(json2protobuf_string__error_is_not_integer_required_for_ ## type) { \ +#define TEST_IMPL_IS_NOT_INTEGER_OR_STRING(type) \ + TEST_IMPL(json2protobuf_string__error_is_not_integer_or_string_required_for_ ## type) { \ + int result; \ + char error_string[256] = {0}; \ + \ + const char *initial_json_string = \ + "{\n" \ + " \"value_" #type "\": [true]\n" \ + "}" \ + ; \ + \ + ProtobufCMessage *protobuf_message = NULL; \ + \ + result = json2protobuf_string( \ + (char *)initial_json_string, \ + 0, \ + &foo__repeated_values__descriptor, \ + &protobuf_message, \ + error_string, \ + sizeof(error_string) \ + ); \ + \ + ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_INTEGER); \ + \ + const char *expected_error_string = \ + "JSON value is not an integer or string required for GPB " #type " value_" #type; \ + ASSERT_STRCMP(error_string, expected_error_string); \ + \ + RETURN_OK(); \ + } + +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(int32) +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(sint32) +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(sfixed32) +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(uint32) +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(fixed32) +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(int64) +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(sint64) +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(sfixed64) +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(uint64) +TEST_IMPL_IS_NOT_INTEGER_OR_STRING(fixed64) + +#define TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(type) \ + TEST_IMPL(json2protobuf_string__error_is_not_convertible_string_for_ ## type) { \ int result; \ char error_string[256] = {0}; \ \ @@ -236,22 +279,362 @@ TEST_IMPL(json2protobuf_string__error_is_not_object_required_for_message) { \ ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_INTEGER); \ \ - const char *expected_error_string = "JSON value is not an integer required for GPB " #type; \ + const char *expected_error_string = \ + "JSON string not successfully converted to value for GPB " #type " value_" #type; \ + ASSERT_STRCMP(error_string, expected_error_string); \ + \ + RETURN_OK(); \ + } + +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(int32) +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(sint32) +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(sfixed32) +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(uint32) +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(fixed32) +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(int64) +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(sint64) +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(sfixed64) +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(uint64) +TEST_IMPL_IS_NOT_CONVERTIBLE_STRING(fixed64) + +TEST_IMPL(json2protobuf_string__string_to_int_convert_success) { + int result; + char initial_json_string[1024]; + + sprintf (initial_json_string, + "{\n" + + " \"value_int32\": [\"%" PRId32 "\", \"0\", \"%" PRId32 "\"],\n" + " \"value_sint32\": [\"%" PRId32 "\", \"0\", \"%" PRId32 "\"],\n" + " \"value_sfixed32\": [\"%" PRId32 "\", \"0\", \"%" PRId32 "\"],\n" + + " \"value_uint32\": [\"0\", \"%" PRIu32 "\"],\n" + " \"value_fixed32\": [\"0\", \"%" PRIu32 "\"],\n" + + " \"value_int64\": [\"%" PRId64 "\", \"0\", \"%" PRId64 "\"],\n" + " \"value_sint64\": [\"%" PRId64 "\", \"0\", \"%" PRId64 "\"],\n" + " \"value_sfixed64\": [\"%" PRId64 "\", \"0\", \"%" PRId64 "\"],\n" + + " \"value_uint64\": [\"0\", \"%" PRIu64 "\"],\n" + " \"value_fixed64\": [\"0\", \"%" PRIu64 "\"]\n" + "}", + INT32_MIN, INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN, INT32_MAX, + UINT32_MAX, UINT32_MAX, + INT64_MIN, INT64_MAX, INT64_MIN, INT64_MAX, INT64_MIN, INT64_MAX, + UINT64_MAX, UINT64_MAX + ); + + ProtobufCMessage *protobuf_message; + + result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, NULL, 0); + ASSERT_ZERO(result); + + Foo__RepeatedValues *repeated_values = (Foo__RepeatedValues *)protobuf_message; + + ASSERT(repeated_values->n_value_int32 == 3); + ASSERT(repeated_values->value_int32[0] == INT32_MIN); + ASSERT(repeated_values->value_int32[1] == 0); + ASSERT(repeated_values->value_int32[2] == INT32_MAX); + + ASSERT(repeated_values->n_value_sint32 == 3); + ASSERT(repeated_values->value_sint32[0] == INT32_MIN); + ASSERT(repeated_values->value_sint32[1] == 0); + ASSERT(repeated_values->value_sint32[2] == INT32_MAX); + + ASSERT(repeated_values->n_value_sfixed32 == 3); + ASSERT(repeated_values->value_sfixed32[0] == INT32_MIN); + ASSERT(repeated_values->value_sfixed32[1] == 0); + ASSERT(repeated_values->value_sfixed32[2] == INT32_MAX); + + ASSERT(repeated_values->n_value_uint32 == 2); + ASSERT(repeated_values->value_uint32[0] == 0); + ASSERT(repeated_values->value_uint32[1] == UINT32_MAX); + + ASSERT(repeated_values->n_value_fixed32 == 2); + ASSERT(repeated_values->value_fixed32[0] == 0); + ASSERT(repeated_values->value_fixed32[1] == UINT32_MAX); + + ASSERT(repeated_values->n_value_int64 == 3); + ASSERT(repeated_values->value_int64[0] == INT64_MIN); + ASSERT(repeated_values->value_int64[1] == 0); + ASSERT(repeated_values->value_int64[2] == INT64_MAX); + + ASSERT(repeated_values->n_value_sint64 == 3); + ASSERT(repeated_values->value_sint64[0] == INT64_MIN); + ASSERT(repeated_values->value_sint64[1] == 0); + ASSERT(repeated_values->value_sint64[2] == INT64_MAX); + + ASSERT(repeated_values->n_value_sfixed64 == 3); + ASSERT(repeated_values->value_sfixed64[0] == INT64_MIN); + ASSERT(repeated_values->value_sfixed64[1] == 0); + ASSERT(repeated_values->value_sfixed64[2] == INT64_MAX); + + ASSERT(repeated_values->n_value_uint64 == 2); + ASSERT(repeated_values->value_uint64[0] == 0); + ASSERT(repeated_values->value_uint64[1] == UINT64_MAX); + + ASSERT(repeated_values->n_value_fixed64 == 2); + ASSERT(repeated_values->value_fixed64[0] == 0); + ASSERT(repeated_values->value_fixed64[1] == UINT64_MAX); + + char *json_string; + result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); + ASSERT_ZERO(result); + ASSERT(json_string); + + const char *expected_json_string = \ + "{\n" + " \"value_int32\": [\n" + " -2147483648,\n" + " 0,\n" + " 2147483647\n" + " ],\n" + " \"value_sint32\": [\n" + " -2147483648,\n" + " 0,\n" + " 2147483647\n" + " ],\n" + " \"value_sfixed32\": [\n" + " -2147483648,\n" + " 0,\n" + " 2147483647\n" + " ],\n" + " \"value_uint32\": [\n" + " 0,\n" + " 4294967295\n" + " ],\n" + " \"value_fixed32\": [\n" + " 0,\n" + " 4294967295\n" + " ],\n" + " \"value_int64\": [\n" + " \"-9223372036854775808\",\n" + " \"0\",\n" + " \"9223372036854775807\"\n" + " ],\n" + " \"value_sint64\": [\n" + " \"-9223372036854775808\",\n" + " \"0\",\n" + " \"9223372036854775807\"\n" + " ],\n" + " \"value_sfixed64\": [\n" + " \"-9223372036854775808\",\n" + " \"0\",\n" + " \"9223372036854775807\"\n" + " ],\n" + " \"value_uint64\": [\n" + " \"0\",\n" + " \"18446744073709551615\"\n" + " ],\n" + " \"value_fixed64\": [\n" + " \"0\",\n" + " \"18446744073709551615\"\n" + " ],\n" + " \"value_float\": [],\n" + " \"value_double\": [],\n" + " \"value_bool\": [],\n" + " \"value_enum\": [],\n" + " \"value_string\": [],\n" + " \"value_bytes\": [],\n" + " \"value_message\": []\n" + "}" + ; + + ASSERT_STRCMP( + json_string, + expected_json_string + ); + + protobuf_c_message_free_unpacked(protobuf_message, NULL); + free(json_string); + + RETURN_OK(); +} + +/* Values bigger that +/- int64 will result in a jansson parsing failure rather than a range error */ +#define TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(type,limit,value) \ + TEST_IMPL(json2protobuf_string__error_out_of_range_number_for_ ## type ## _ ## limit) { \ + int result; \ + char error_string[256] = {0}; \ + char initial_json_string[256]; \ + \ + sprintf (initial_json_string, \ + "{\n" \ + " \"value_" #type "\": [%lld]\n" \ + "}", value \ + ); \ + \ + ProtobufCMessage *protobuf_message = NULL; \ + \ + result = json2protobuf_string( \ + (char *)initial_json_string, \ + 0, \ + &foo__repeated_values__descriptor, \ + &protobuf_message, \ + error_string, \ + sizeof(error_string) \ + ); \ + \ + ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_INTEGER); \ + \ + const char *expected_error_string = \ + "JSON value is not within range for GPB " #type " value_" #type \ + ; \ + ASSERT_STRCMP(error_string, expected_error_string); \ + \ + RETURN_OK(); \ + } + +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(int32,min,-2147483649LL) +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(int32,max,2147483648LL) +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(sint32,min,-2147483649LL) +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(sint32,max,2147483648LL) +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(sfixed32,min,-2147483649LL) +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(sfixed32,max,2147483648LL) +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(uint32,min,-1LL) +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(uint32,max,4294967296LL) +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_NUMBER(uint64,min,-1LL) + + +/* Values bigger than +/- int64 will result in a conversion failure rather than a range error */ +#define TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(type,limit,value) \ + TEST_IMPL(json2protobuf_string__error_out_of_range_string_for_ ## type ## _ ## limit) { \ + int result; \ + char error_string[256] = {0}; \ + \ + const char *initial_json_string = \ + "{\n" \ + " \"value_" #type "\": [\"" value "\"]\n" \ + "}" \ + ; \ + \ + ProtobufCMessage *protobuf_message = NULL; \ + \ + result = json2protobuf_string( \ + (char *)initial_json_string, \ + 0, \ + &foo__repeated_values__descriptor, \ + &protobuf_message, \ + error_string, \ + sizeof(error_string) \ + ); \ + \ + ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_INTEGER); \ + \ + const char *expected_error_string = \ + "JSON value is not within range for GPB " #type " value_" #type \ + ; \ ASSERT_STRCMP(error_string, expected_error_string); \ \ RETURN_OK(); \ } -TEST_IMPL_IS_NOT_INTEGER(int32) -TEST_IMPL_IS_NOT_INTEGER(sint32) -TEST_IMPL_IS_NOT_INTEGER(sfixed32) -TEST_IMPL_IS_NOT_INTEGER(uint32) -TEST_IMPL_IS_NOT_INTEGER(fixed32) -TEST_IMPL_IS_NOT_INTEGER(int64) -TEST_IMPL_IS_NOT_INTEGER(sint64) -TEST_IMPL_IS_NOT_INTEGER(sfixed64) -TEST_IMPL_IS_NOT_INTEGER(uint64) -TEST_IMPL_IS_NOT_INTEGER(fixed64) +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(int32,min,"-2147483649") +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(int32,max,"2147483648") +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(sint32,min,"-2147483649") +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(sint32,max,"2147483648") +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(sfixed32,min,"-2147483649") +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(sfixed32,max,"2147483648") +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(uint32,min,"-1") +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(uint32,max,"4294967296") +TEST_IMPL_OUT_OF_RANGE_CHECK_WITHIN_INT64_STRING(uint64,min,"-1") + +#define TEST_IMPL_IS_TOO_BIG_TO_CONVERT_STRING(type,limit,value) \ + TEST_IMPL(json2protobuf_string__error_is_too_big_to_convert_ ## type ## _ ## limit) { \ + int result; \ + char error_string[256] = {0}; \ + \ + const char *initial_json_string = \ + "{\n" \ + " \"value_" #type "\": [\"" value "\"]\n" \ + "}" \ + ; \ + \ + ProtobufCMessage *protobuf_message = NULL; \ + \ + result = json2protobuf_string( \ + (char *)initial_json_string, \ + 0, \ + &foo__repeated_values__descriptor, \ + &protobuf_message, \ + error_string, \ + sizeof(error_string) \ + ); \ + \ + ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_INTEGER); \ + \ + const char *expected_error_string = \ + "JSON string not successfully converted to value for GPB " #type " value_" #type \ + ; \ + ASSERT_STRCMP(error_string, expected_error_string); \ + \ + RETURN_OK(); \ + } + +TEST_IMPL_IS_TOO_BIG_TO_CONVERT_STRING(int64,min,"-9223372036854775809") +TEST_IMPL_IS_TOO_BIG_TO_CONVERT_STRING(int64,max,"9223372036854775809") +TEST_IMPL_IS_TOO_BIG_TO_CONVERT_STRING(sint64,min,"-9223372036854775809") +TEST_IMPL_IS_TOO_BIG_TO_CONVERT_STRING(sint64,max,"9223372036854775809") +TEST_IMPL_IS_TOO_BIG_TO_CONVERT_STRING(sfixed64,min,"-9223372036854775809") +TEST_IMPL_IS_TOO_BIG_TO_CONVERT_STRING(sfixed64,max,"9223372036854775809") +TEST_IMPL_IS_TOO_BIG_TO_CONVERT_STRING(uint64,max,"18446744073709551616") +TEST_IMPL_IS_TOO_BIG_TO_CONVERT_STRING(fixed64,max,"18446744073709551616") + +/* Confirm that number values larger than INT64_MAX generate a parsing failure */ +TEST_IMPL(json2protobuf_string__error_is_too_big_to_convert_positive) { + int result; + char error_string[256] = {0}; + + const char *initial_json_string = \ + "{\n" + " \"value_uint64\": [9223372036854775808]\n" + "}" + ; + + ProtobufCMessage *protobuf_message = NULL; + + result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, error_string, sizeof(error_string)); + ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_PARSE_STRING); + + const char *expected_error_string = + "JSON parsing error at line 2 column 38 (position 40): too big integer near '9223372036854775808'" + ; + + ASSERT_STRCMP( + error_string, + expected_error_string + ); + + RETURN_OK(); +} + +/* Confirm that number values below -INT64_MAX generate a parsing failure */ +TEST_IMPL(json2protobuf_string__error_is_too_big_to_convert_negative) { + int result; + char error_string[256] = {0}; + + const char *initial_json_string = \ + "{\n" + " \"value_int64\": [-9223372036854775809]\n" + "}" + ; + + ProtobufCMessage *protobuf_message = NULL; + + result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, error_string, sizeof(error_string)); + ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_PARSE_STRING); + + const char *expected_error_string = + "JSON parsing error at line 2 column 38 (position 40): too big negative integer near '-9223372036854775809'" + ; + + ASSERT_STRCMP( + error_string, + expected_error_string + ); + + RETURN_OK(); +} TEST_IMPL(json2protobuf_string__error_is_not_real_number_required_for_float) { int result; diff --git a/test/test-list.h b/test/test-list.h index ae64e51..ce1f4cd 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -38,16 +38,55 @@ TEST_DECLARE(json2protobuf_string__error_unknown_field) TEST_DECLARE(json2protobuf_string__error_unknown_enum_value) TEST_DECLARE(json2protobuf_string__error_required_is_missing) TEST_DECLARE(json2protobuf_string__error_is_not_object_required_for_message) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_int32) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_sint32) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_sfixed32) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_uint32) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_fixed32) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_int64) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_sint64) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_sfixed64) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_uint64) -TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_fixed64) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_int32) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_sint32) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_sfixed32) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_uint32) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_fixed32) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_int64) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_sint64) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_sfixed64) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_uint64) +TEST_DECLARE(json2protobuf_string__error_is_not_integer_or_string_required_for_fixed64) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_int32) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_sint32) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_sfixed32) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_uint32) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_fixed32) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_int64) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_sint64) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_sfixed64) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_uint64) +TEST_DECLARE(json2protobuf_string__error_is_not_convertible_string_for_fixed64) +TEST_DECLARE(json2protobuf_string__string_to_int_convert_success) +TEST_DECLARE(json2protobuf_string__error_out_of_range_number_for_int32_min) +TEST_DECLARE(json2protobuf_string__error_out_of_range_number_for_int32_max) +TEST_DECLARE(json2protobuf_string__error_out_of_range_number_for_sint32_min) +TEST_DECLARE(json2protobuf_string__error_out_of_range_number_for_sint32_max) +TEST_DECLARE(json2protobuf_string__error_out_of_range_number_for_sfixed32_min) +TEST_DECLARE(json2protobuf_string__error_out_of_range_number_for_sfixed32_max) +TEST_DECLARE(json2protobuf_string__error_out_of_range_number_for_uint32_min) +TEST_DECLARE(json2protobuf_string__error_out_of_range_number_for_uint32_max) +TEST_DECLARE(json2protobuf_string__error_out_of_range_number_for_uint64_min) +TEST_DECLARE(json2protobuf_string__error_out_of_range_string_for_int32_min) +TEST_DECLARE(json2protobuf_string__error_out_of_range_string_for_int32_max) +TEST_DECLARE(json2protobuf_string__error_out_of_range_string_for_sint32_min) +TEST_DECLARE(json2protobuf_string__error_out_of_range_string_for_sint32_max) +TEST_DECLARE(json2protobuf_string__error_out_of_range_string_for_sfixed32_min) +TEST_DECLARE(json2protobuf_string__error_out_of_range_string_for_sfixed32_max) +TEST_DECLARE(json2protobuf_string__error_out_of_range_string_for_uint32_min) +TEST_DECLARE(json2protobuf_string__error_out_of_range_string_for_uint32_max) +TEST_DECLARE(json2protobuf_string__error_out_of_range_string_for_uint64_min) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_int64_min) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_int64_max) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_sint64_min) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_sint64_max) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_sfixed64_min) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_sfixed64_max) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_uint64_max) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_fixed64_max) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_positive) +TEST_DECLARE(json2protobuf_string__error_is_too_big_to_convert_negative) TEST_DECLARE(json2protobuf_string__error_is_not_real_number_required_for_float) TEST_DECLARE(json2protobuf_string__error_is_not_real_number_required_for_double) TEST_DECLARE(json2protobuf_string__error_is_not_boolean_required_for_bool) @@ -99,16 +138,55 @@ TASK_LIST_START TEST_ENTRY(json2protobuf_string__error_unknown_enum_value) TEST_ENTRY(json2protobuf_string__error_required_is_missing) TEST_ENTRY(json2protobuf_string__error_is_not_object_required_for_message) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_int32) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_sint32) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_sfixed32) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_uint32) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_fixed32) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_int64) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_sint64) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_sfixed64) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_uint64) - TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_fixed64) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_int32) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_sint32) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_sfixed32) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_uint32) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_fixed32) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_int64) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_sint64) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_sfixed64) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_uint64) + TEST_ENTRY(json2protobuf_string__error_is_not_integer_or_string_required_for_fixed64) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_int32) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_sint32) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_sfixed32) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_uint32) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_fixed32) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_int64) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_sint64) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_sfixed64) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_uint64) + TEST_ENTRY(json2protobuf_string__error_is_not_convertible_string_for_fixed64) + TEST_ENTRY(json2protobuf_string__string_to_int_convert_success) + TEST_ENTRY(json2protobuf_string__error_out_of_range_number_for_int32_min) + TEST_ENTRY(json2protobuf_string__error_out_of_range_number_for_int32_max) + TEST_ENTRY(json2protobuf_string__error_out_of_range_number_for_sint32_min) + TEST_ENTRY(json2protobuf_string__error_out_of_range_number_for_sint32_max) + TEST_ENTRY(json2protobuf_string__error_out_of_range_number_for_sfixed32_min) + TEST_ENTRY(json2protobuf_string__error_out_of_range_number_for_sfixed32_max) + TEST_ENTRY(json2protobuf_string__error_out_of_range_number_for_uint32_min) + TEST_ENTRY(json2protobuf_string__error_out_of_range_number_for_uint32_max) + TEST_ENTRY(json2protobuf_string__error_out_of_range_number_for_uint64_min) + TEST_ENTRY(json2protobuf_string__error_out_of_range_string_for_int32_min) + TEST_ENTRY(json2protobuf_string__error_out_of_range_string_for_int32_max) + TEST_ENTRY(json2protobuf_string__error_out_of_range_string_for_sint32_min) + TEST_ENTRY(json2protobuf_string__error_out_of_range_string_for_sint32_max) + TEST_ENTRY(json2protobuf_string__error_out_of_range_string_for_sfixed32_min) + TEST_ENTRY(json2protobuf_string__error_out_of_range_string_for_sfixed32_max) + TEST_ENTRY(json2protobuf_string__error_out_of_range_string_for_uint32_min) + TEST_ENTRY(json2protobuf_string__error_out_of_range_string_for_uint32_max) + TEST_ENTRY(json2protobuf_string__error_out_of_range_string_for_uint64_min) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_int64_min) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_int64_max) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_sint64_min) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_sint64_max) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_sfixed64_min) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_sfixed64_max) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_uint64_max) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_fixed64_max) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_positive) + TEST_ENTRY(json2protobuf_string__error_is_too_big_to_convert_negative) TEST_ENTRY(json2protobuf_string__error_is_not_real_number_required_for_float) TEST_ENTRY(json2protobuf_string__error_is_not_real_number_required_for_double) TEST_ENTRY(json2protobuf_string__error_is_not_boolean_required_for_bool)