diff options
Diffstat (limited to 'tests')
201 files changed, 6865 insertions, 372 deletions
diff --git a/tests/Makefile b/tests/Makefile index 3c4e0b0..cee6bf6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -10,12 +10,12 @@ coverage: # LCOV does not like the newer gcov format scons CC=gcc-4.6 CXX=gcc-4.6 - # We are only interested in pb_encode.o and pb_decode.o - find build -name '*.gcda' -and \! \( -name '*pb_encode*' -or -name '*pb_decode*' \) -exec rm '{}' \; - # Collect the data mkdir build/coverage lcov --base-directory . --directory build/ --gcov-tool gcov-4.6 -c -o build/coverage/nanopb.info + # Remove the test code from results + lcov -r build/coverage/nanopb.info '*tests*' -o build/coverage/nanopb.info + # Generate HTML genhtml -o build/coverage build/coverage/nanopb.info diff --git a/tests/SConstruct b/tests/SConstruct index 7a27844..f998024 100644 --- a/tests/SConstruct +++ b/tests/SConstruct @@ -57,6 +57,7 @@ if not env.GetOption('clean'): if not stdbool or not stdint or not stddef or not string: conf.env.Append(CPPDEFINES = {'PB_SYSTEM_HEADER': '\\"pb_syshdr.h\\"'}) conf.env.Append(CPPPATH = "#../extra") + conf.env.Append(SYSHDR = '\\"pb_syshdr.h\\"') if stdbool: conf.env.Append(CPPDEFINES = {'HAVE_STDBOOL_H': 1}) if stdint: conf.env.Append(CPPDEFINES = {'HAVE_STDINT_H': 1}) @@ -71,6 +72,11 @@ if not env.GetOption('clean'): else: conf.env.Append(PROTOCPATH = '/usr/include') + # Check protoc version + status, output = conf.TryAction('$PROTOC --version > $TARGET') + if status: + conf.env['PROTOC_VERSION'] = output + # Check if libmudflap is available (only with GCC) if 'gcc' in env['CC']: if conf.CheckLib('mudflap'): @@ -87,7 +93,9 @@ if not env.GetOption('clean'): conf.env.Append(CORECFLAGS = extra) # Check if we can use undefined behaviour sanitizer (only with clang) - extra = '-fsanitize=undefined ' + # TODO: Fuzz test triggers the bool sanitizer, figure out whether to + # modify the fuzz test or to keep ignoring the check. + extra = '-fsanitize=undefined,integer -fno-sanitize-recover=undefined,integer -fsanitize-recover=bool ' if 'clang' in env['CC']: if conf.CheckCCFLAGS(extra, linkflags = extra): conf.env.Append(CORECFLAGS = extra) @@ -101,7 +109,7 @@ if 'gcc' in env['CC']: # GNU Compiler Collection # Debug info, warnings as errors - env.Append(CFLAGS = '-ansi -pedantic -g -Wall -Werror -fprofile-arcs -ftest-coverage -fstack-protector-all') + env.Append(CFLAGS = '-ansi -pedantic -g -Wall -Werror -fprofile-arcs -ftest-coverage ') env.Append(CORECFLAGS = '-Wextra') env.Append(LINKFLAGS = '-g --coverage') @@ -120,9 +128,6 @@ elif 'cl' in env['CC']: # More strict checks on the nanopb core env.Append(CORECFLAGS = '/W4') - - # PB_RETURN_ERROR triggers C4127 because of while(0) - env.Append(CFLAGS = '/wd4127') elif 'tcc' in env['CC']: # Tiny C Compiler env.Append(CFLAGS = '-Wall -Werror -g') @@ -135,12 +140,18 @@ elif 'g++' in env['CXX'] or 'gcc' in env['CXX']: env.Append(CXXFLAGS = '-g -Wall -Werror -Wextra -Wno-missing-field-initializers') elif 'cl' in env['CXX']: env.Append(CXXFLAGS = '/Zi /W2 /WX') - + # Now include the SConscript files from all subdirectories import os.path env['VARIANT_DIR'] = 'build' env['BUILD'] = '#' + env['VARIANT_DIR'] env['COMMON'] = '#' + env['VARIANT_DIR'] + '/common' -for subdir in Glob('*/SConscript'): + +# Include common/SConscript first to make sure its exports are available +# to other SConscripts. +SConscript("common/SConscript", exports = 'env', variant_dir = env['VARIANT_DIR'] + '/common') + +for subdir in Glob('*/SConscript') + Glob('regression/*/SConscript'): + if str(subdir).startswith("common"): continue SConscript(subdir, exports = 'env', variant_dir = env['VARIANT_DIR'] + '/' + os.path.dirname(str(subdir))) diff --git a/tests/alltypes/SConscript b/tests/alltypes/SConscript index 9c9072b..13acd4e 100644 --- a/tests/alltypes/SConscript +++ b/tests/alltypes/SConscript @@ -4,8 +4,8 @@ Import("env") env.NanopbProto(["alltypes", "alltypes.options"]) -enc = env.Program(["encode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_encode.o"]) -dec = env.Program(["decode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_decode.o"]) +enc = env.Program(["encode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) +dec = env.Program(["decode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) # Test the round-trip from nanopb encoder to nanopb decoder env.RunTest(enc) @@ -32,4 +32,14 @@ env.Encode("optionals.output.recoded", MESSAGE='AllTypes') env.Compare(["optionals.output", "optionals.output.recoded"]) +# And for the _zero initializer +env.RunTest("zeroinit.output", enc, ARGS = ['2']) +env.RunTest("zeroinit.decout", [dec, "zeroinit.output"], ARGS = ['2']) +env.Decode("zeroinit.output.decoded", + ["zeroinit.output", "alltypes.proto"], + MESSAGE='AllTypes') +env.Encode("zeroinit.output.recoded", + ["zeroinit.output.decoded", "alltypes.proto"], + MESSAGE='AllTypes') +env.Compare(["zeroinit.output", "zeroinit.output.recoded"]) diff --git a/tests/alltypes/alltypes.options b/tests/alltypes/alltypes.options index b31e3cf..0d5ab12 100644 --- a/tests/alltypes/alltypes.options +++ b/tests/alltypes/alltypes.options @@ -1,3 +1,3 @@ * max_size:16 * max_count:5 - +*.*fbytes fixed_length:true max_size:4 diff --git a/tests/alltypes/alltypes.proto b/tests/alltypes/alltypes.proto index db83c9a..2377180 100644 --- a/tests/alltypes/alltypes.proto +++ b/tests/alltypes/alltypes.proto @@ -1,3 +1,6 @@ +syntax = "proto2"; +// package name placeholder + message SubMessage { required string substuff1 = 1 [default = "1"]; required int32 substuff2 = 2 [default = 2]; @@ -55,7 +58,7 @@ message AllTypes { required SubMessage req_submsg = 16; required MyEnum req_enum = 17; required EmptyMessage req_emptymsg = 18; - + required bytes req_fbytes = 19; repeated int32 rep_int32 = 21 [packed = true]; repeated int64 rep_int64 = 22 [packed = true]; @@ -78,6 +81,7 @@ message AllTypes { repeated SubMessage rep_submsg = 36; repeated MyEnum rep_enum = 37 [packed = true]; repeated EmptyMessage rep_emptymsg = 38; + repeated bytes rep_fbytes = 39; optional int32 opt_int32 = 41 [default = 4041]; optional int64 opt_int64 = 42 [default = 4042]; @@ -96,11 +100,18 @@ message AllTypes { optional double opt_double = 53 [default = 4053]; optional string opt_string = 54 [default = "4054"]; - optional bytes opt_bytes = 55 [default = "4055"]; + optional bytes opt_bytes = 55 [default = "\x34\x5C\x00\xff"]; optional SubMessage opt_submsg = 56; optional MyEnum opt_enum = 57 [default = Second]; optional EmptyMessage opt_emptymsg = 58; + optional bytes opt_fbytes = 59 [default = "4059"]; + oneof oneof + { + SubMessage oneof_msg1 = 60; + EmptyMessage oneof_msg2 = 61; + } + // Check that extreme integer values are handled correctly required Limits req_limits = 98; diff --git a/tests/alltypes/decode_alltypes.c b/tests/alltypes/decode_alltypes.c index db72bb9..b74121f 100644 --- a/tests/alltypes/decode_alltypes.c +++ b/tests/alltypes/decode_alltypes.c @@ -19,65 +19,85 @@ the decoding and checks the fields. */ bool check_alltypes(pb_istream_t *stream, int mode) { - AllTypes alltypes; + /* Uses _init_default to just make sure that it works. */ + AllTypes alltypes = AllTypes_init_default; /* Fill with garbage to better detect initialization errors */ memset(&alltypes, 0xAA, sizeof(alltypes)); + alltypes.extensions = 0; if (!pb_decode(stream, AllTypes_fields, &alltypes)) return false; - TEST(alltypes.req_int32 == -1001); - TEST(alltypes.req_int64 == -1002); - TEST(alltypes.req_uint32 == 1003); - TEST(alltypes.req_uint64 == 1004); - TEST(alltypes.req_sint32 == -1005); - TEST(alltypes.req_sint64 == -1006); - TEST(alltypes.req_bool == true); - - TEST(alltypes.req_fixed32 == 1008); - TEST(alltypes.req_sfixed32 == -1009); - TEST(alltypes.req_float == 1010.0f); - - TEST(alltypes.req_fixed64 == 1011); - TEST(alltypes.req_sfixed64 == -1012); - TEST(alltypes.req_double == 1013.0f); - - TEST(strcmp(alltypes.req_string, "1014") == 0); - TEST(alltypes.req_bytes.size == 4); - TEST(memcmp(alltypes.req_bytes.bytes, "1015", 4) == 0); - TEST(strcmp(alltypes.req_submsg.substuff1, "1016") == 0); - TEST(alltypes.req_submsg.substuff2 == 1016); - TEST(alltypes.req_submsg.substuff3 == 3); - TEST(alltypes.req_enum == MyEnum_Truth); - - TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0); - TEST(alltypes.rep_int64_count == 5 && alltypes.rep_int64[4] == -2002 && alltypes.rep_int64[0] == 0); - TEST(alltypes.rep_uint32_count == 5 && alltypes.rep_uint32[4] == 2003 && alltypes.rep_uint32[0] == 0); - TEST(alltypes.rep_uint64_count == 5 && alltypes.rep_uint64[4] == 2004 && alltypes.rep_uint64[0] == 0); - TEST(alltypes.rep_sint32_count == 5 && alltypes.rep_sint32[4] == -2005 && alltypes.rep_sint32[0] == 0); - TEST(alltypes.rep_sint64_count == 5 && alltypes.rep_sint64[4] == -2006 && alltypes.rep_sint64[0] == 0); - TEST(alltypes.rep_bool_count == 5 && alltypes.rep_bool[4] == true && alltypes.rep_bool[0] == false); - - TEST(alltypes.rep_fixed32_count == 5 && alltypes.rep_fixed32[4] == 2008 && alltypes.rep_fixed32[0] == 0); - TEST(alltypes.rep_sfixed32_count == 5 && alltypes.rep_sfixed32[4] == -2009 && alltypes.rep_sfixed32[0] == 0); - TEST(alltypes.rep_float_count == 5 && alltypes.rep_float[4] == 2010.0f && alltypes.rep_float[0] == 0.0f); - - TEST(alltypes.rep_fixed64_count == 5 && alltypes.rep_fixed64[4] == 2011 && alltypes.rep_fixed64[0] == 0); - TEST(alltypes.rep_sfixed64_count == 5 && alltypes.rep_sfixed64[4] == -2012 && alltypes.rep_sfixed64[0] == 0); - TEST(alltypes.rep_double_count == 5 && alltypes.rep_double[4] == 2013.0 && alltypes.rep_double[0] == 0.0); - - TEST(alltypes.rep_string_count == 5 && strcmp(alltypes.rep_string[4], "2014") == 0 && alltypes.rep_string[0][0] == '\0'); - TEST(alltypes.rep_bytes_count == 5 && alltypes.rep_bytes[4].size == 4 && alltypes.rep_bytes[0].size == 0); - TEST(memcmp(alltypes.rep_bytes[4].bytes, "2015", 4) == 0); + if (mode == 0 || mode == 1) + { + TEST(alltypes.req_int32 == -1001); + TEST(alltypes.req_int64 == -1002); + TEST(alltypes.req_uint32 == 1003); + TEST(alltypes.req_uint64 == 1004); + TEST(alltypes.req_sint32 == -1005); + TEST(alltypes.req_sint64 == -1006); + TEST(alltypes.req_bool == true); + + TEST(alltypes.req_fixed32 == 1008); + TEST(alltypes.req_sfixed32 == -1009); + TEST(alltypes.req_float == 1010.0f); + + TEST(alltypes.req_fixed64 == 1011); + TEST(alltypes.req_sfixed64 == -1012); + TEST(alltypes.req_double == 1013.0f); + + TEST(strcmp(alltypes.req_string, "1014") == 0); + TEST(alltypes.req_bytes.size == 4); + TEST(memcmp(alltypes.req_bytes.bytes, "1015", 4) == 0); + TEST(strcmp(alltypes.req_submsg.substuff1, "1016") == 0); + TEST(alltypes.req_submsg.substuff2 == 1016); + TEST(alltypes.req_submsg.substuff3 == 3); + TEST(alltypes.req_enum == MyEnum_Truth); + TEST(memcmp(alltypes.req_fbytes, "1019", 4) == 0); + + TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0); + TEST(alltypes.rep_int64_count == 5 && alltypes.rep_int64[4] == -2002 && alltypes.rep_int64[0] == 0); + TEST(alltypes.rep_uint32_count == 5 && alltypes.rep_uint32[4] == 2003 && alltypes.rep_uint32[0] == 0); + TEST(alltypes.rep_uint64_count == 5 && alltypes.rep_uint64[4] == 2004 && alltypes.rep_uint64[0] == 0); + TEST(alltypes.rep_sint32_count == 5 && alltypes.rep_sint32[4] == -2005 && alltypes.rep_sint32[0] == 0); + TEST(alltypes.rep_sint64_count == 5 && alltypes.rep_sint64[4] == -2006 && alltypes.rep_sint64[0] == 0); + TEST(alltypes.rep_bool_count == 5 && alltypes.rep_bool[4] == true && alltypes.rep_bool[0] == false); + + TEST(alltypes.rep_fixed32_count == 5 && alltypes.rep_fixed32[4] == 2008 && alltypes.rep_fixed32[0] == 0); + TEST(alltypes.rep_sfixed32_count == 5 && alltypes.rep_sfixed32[4] == -2009 && alltypes.rep_sfixed32[0] == 0); + TEST(alltypes.rep_float_count == 5 && alltypes.rep_float[4] == 2010.0f && alltypes.rep_float[0] == 0.0f); + + TEST(alltypes.rep_fixed64_count == 5 && alltypes.rep_fixed64[4] == 2011 && alltypes.rep_fixed64[0] == 0); + TEST(alltypes.rep_sfixed64_count == 5 && alltypes.rep_sfixed64[4] == -2012 && alltypes.rep_sfixed64[0] == 0); + TEST(alltypes.rep_double_count == 5 && alltypes.rep_double[4] == 2013.0 && alltypes.rep_double[0] == 0.0); + + TEST(alltypes.rep_string_count == 5 && strcmp(alltypes.rep_string[4], "2014") == 0 && alltypes.rep_string[0][0] == '\0'); + TEST(alltypes.rep_bytes_count == 5 && alltypes.rep_bytes[4].size == 4 && alltypes.rep_bytes[0].size == 0); + TEST(memcmp(alltypes.rep_bytes[4].bytes, "2015", 4) == 0); - TEST(alltypes.rep_submsg_count == 5); - TEST(strcmp(alltypes.rep_submsg[4].substuff1, "2016") == 0 && alltypes.rep_submsg[0].substuff1[0] == '\0'); - TEST(alltypes.rep_submsg[4].substuff2 == 2016 && alltypes.rep_submsg[0].substuff2 == 0); - TEST(alltypes.rep_submsg[4].substuff3 == 2016 && alltypes.rep_submsg[0].substuff3 == 3); - - TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero); - TEST(alltypes.rep_emptymsg_count == 5); + TEST(alltypes.rep_submsg_count == 5); + TEST(strcmp(alltypes.rep_submsg[4].substuff1, "2016") == 0 && alltypes.rep_submsg[0].substuff1[0] == '\0'); + TEST(alltypes.rep_submsg[4].substuff2 == 2016 && alltypes.rep_submsg[0].substuff2 == 0); + TEST(alltypes.rep_submsg[4].substuff3 == 2016 && alltypes.rep_submsg[0].substuff3 == 3); + + TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero); + TEST(alltypes.rep_emptymsg_count == 5); + TEST(alltypes.rep_fbytes_count == 5); + TEST(alltypes.rep_fbytes[0][0] == 0 && alltypes.rep_fbytes[0][3] == 0); + TEST(memcmp(alltypes.rep_fbytes[4], "2019", 4) == 0); + + TEST(alltypes.req_limits.int32_min == INT32_MIN); + TEST(alltypes.req_limits.int32_max == INT32_MAX); + TEST(alltypes.req_limits.uint32_min == 0); + TEST(alltypes.req_limits.uint32_max == UINT32_MAX); + TEST(alltypes.req_limits.int64_min == INT64_MIN); + TEST(alltypes.req_limits.int64_max == INT64_MAX); + TEST(alltypes.req_limits.uint64_min == 0); + TEST(alltypes.req_limits.uint64_max == UINT64_MAX); + TEST(alltypes.req_limits.enum_min == HugeEnum_Negative); + TEST(alltypes.req_limits.enum_max == HugeEnum_Positive); + } if (mode == 0) { @@ -115,7 +135,7 @@ bool check_alltypes(pb_istream_t *stream, int mode) TEST(strcmp(alltypes.opt_string, "4054") == 0); TEST(alltypes.has_opt_bytes == false); TEST(alltypes.opt_bytes.size == 4); - TEST(memcmp(alltypes.opt_bytes.bytes, "4055", 4) == 0); + TEST(memcmp(alltypes.opt_bytes.bytes, "\x34\x5C\x00\xff", 4) == 0); TEST(alltypes.has_opt_submsg == false); TEST(strcmp(alltypes.opt_submsg.substuff1, "1") == 0); TEST(alltypes.opt_submsg.substuff2 == 2); @@ -123,8 +143,12 @@ bool check_alltypes(pb_istream_t *stream, int mode) TEST(alltypes.has_opt_enum == false); TEST(alltypes.opt_enum == MyEnum_Second); TEST(alltypes.has_opt_emptymsg == false); + TEST(alltypes.has_opt_fbytes == false); + TEST(memcmp(alltypes.opt_fbytes, "4059", 4) == 0); + + TEST(alltypes.which_oneof == 0); } - else + else if (mode == 1) { /* Expect filled-in values */ TEST(alltypes.has_opt_int32 == true); @@ -168,18 +192,88 @@ bool check_alltypes(pb_istream_t *stream, int mode) TEST(alltypes.has_opt_enum == true); TEST(alltypes.opt_enum == MyEnum_Truth); TEST(alltypes.has_opt_emptymsg == true); + TEST(alltypes.has_opt_fbytes == true); + TEST(memcmp(alltypes.opt_fbytes, "3059", 4) == 0); + + TEST(alltypes.which_oneof == AllTypes_oneof_msg1_tag); + TEST(strcmp(alltypes.oneof.oneof_msg1.substuff1, "4059") == 0); + TEST(alltypes.oneof.oneof_msg1.substuff2 == 4059); } + else if (mode == 2) + { + /* Expect zero values */ + TEST(alltypes.req_int32 == 0); + TEST(alltypes.req_int64 == 0); + TEST(alltypes.req_uint32 == 0); + TEST(alltypes.req_uint64 == 0); + TEST(alltypes.req_sint32 == 0); + TEST(alltypes.req_sint64 == 0); + TEST(alltypes.req_bool == false); + + TEST(alltypes.req_fixed32 == 0); + TEST(alltypes.req_sfixed32 == 0); + TEST(alltypes.req_float == 0.0f); + + TEST(alltypes.req_fixed64 == 0); + TEST(alltypes.req_sfixed64 == 0); + TEST(alltypes.req_double == 0.0f); + + TEST(strcmp(alltypes.req_string, "") == 0); + TEST(alltypes.req_bytes.size == 0); + TEST(strcmp(alltypes.req_submsg.substuff1, "") == 0); + TEST(alltypes.req_submsg.substuff2 == 0); + TEST(alltypes.req_enum == MyEnum_Zero); + + TEST(alltypes.rep_int32_count == 0); + TEST(alltypes.rep_int64_count == 0); + TEST(alltypes.rep_uint32_count == 0); + TEST(alltypes.rep_uint64_count == 0); + TEST(alltypes.rep_sint32_count == 0); + TEST(alltypes.rep_sint64_count == 0); + TEST(alltypes.rep_bool_count == 0); + + TEST(alltypes.rep_fixed32_count == 0); + TEST(alltypes.rep_sfixed32_count == 0); + TEST(alltypes.rep_float_count == 0); + + TEST(alltypes.rep_fixed64_count == 0); + TEST(alltypes.rep_sfixed64_count == 0); + TEST(alltypes.rep_double_count == 0); + + TEST(alltypes.rep_string_count == 0); + TEST(alltypes.rep_bytes_count == 0); + + TEST(alltypes.rep_submsg_count == 0); + + TEST(alltypes.rep_enum_count == 0); + TEST(alltypes.rep_emptymsg_count == 0); + TEST(alltypes.rep_fbytes_count == 0); - TEST(alltypes.req_limits.int32_min == INT32_MIN); - TEST(alltypes.req_limits.int32_max == INT32_MAX); - TEST(alltypes.req_limits.uint32_min == 0); - TEST(alltypes.req_limits.uint32_max == UINT32_MAX); - TEST(alltypes.req_limits.int64_min == INT64_MIN); - TEST(alltypes.req_limits.int64_max == INT64_MAX); - TEST(alltypes.req_limits.uint64_min == 0); - TEST(alltypes.req_limits.uint64_max == UINT64_MAX); - TEST(alltypes.req_limits.enum_min == HugeEnum_Negative); - TEST(alltypes.req_limits.enum_max == HugeEnum_Positive); + TEST(alltypes.has_opt_int32 == false); + TEST(alltypes.has_opt_int64 == false); + TEST(alltypes.has_opt_uint32 == false); + TEST(alltypes.has_opt_uint64 == false); + TEST(alltypes.has_opt_sint32 == false); + TEST(alltypes.has_opt_sint64 == false); + TEST(alltypes.has_opt_bool == false); + + TEST(alltypes.has_opt_fixed32 == false); + TEST(alltypes.has_opt_sfixed32 == false); + TEST(alltypes.has_opt_float == false); + + TEST(alltypes.has_opt_fixed64 == false); + TEST(alltypes.has_opt_sfixed64 == false); + TEST(alltypes.has_opt_double == false); + + TEST(alltypes.has_opt_string == false); + TEST(alltypes.has_opt_bytes == false); + TEST(alltypes.has_opt_submsg == false); + TEST(alltypes.has_opt_enum == false); + TEST(alltypes.has_opt_emptymsg == false); + TEST(alltypes.has_opt_fbytes == false); + + TEST(alltypes.which_oneof == 0); + } TEST(alltypes.end == 1099); diff --git a/tests/alltypes/encode_alltypes.c b/tests/alltypes/encode_alltypes.c index fa8eec9..15ea7b8 100644 --- a/tests/alltypes/encode_alltypes.c +++ b/tests/alltypes/encode_alltypes.c @@ -13,72 +13,79 @@ int main(int argc, char **argv) int mode = (argc > 1) ? atoi(argv[1]) : 0; /* Initialize the structure with constants */ - AllTypes alltypes = {0}; + AllTypes alltypes = AllTypes_init_zero; - alltypes.req_int32 = -1001; - alltypes.req_int64 = -1002; - alltypes.req_uint32 = 1003; - alltypes.req_uint64 = 1004; - alltypes.req_sint32 = -1005; - alltypes.req_sint64 = -1006; - alltypes.req_bool = true; - - alltypes.req_fixed32 = 1008; - alltypes.req_sfixed32 = -1009; - alltypes.req_float = 1010.0f; - - alltypes.req_fixed64 = 1011; - alltypes.req_sfixed64 = -1012; - alltypes.req_double = 1013.0; - - strcpy(alltypes.req_string, "1014"); - alltypes.req_bytes.size = 4; - memcpy(alltypes.req_bytes.bytes, "1015", 4); - strcpy(alltypes.req_submsg.substuff1, "1016"); - alltypes.req_submsg.substuff2 = 1016; - alltypes.req_enum = MyEnum_Truth; - - alltypes.rep_int32_count = 5; alltypes.rep_int32[4] = -2001; - alltypes.rep_int64_count = 5; alltypes.rep_int64[4] = -2002; - alltypes.rep_uint32_count = 5; alltypes.rep_uint32[4] = 2003; - alltypes.rep_uint64_count = 5; alltypes.rep_uint64[4] = 2004; - alltypes.rep_sint32_count = 5; alltypes.rep_sint32[4] = -2005; - alltypes.rep_sint64_count = 5; alltypes.rep_sint64[4] = -2006; - alltypes.rep_bool_count = 5; alltypes.rep_bool[4] = true; - - alltypes.rep_fixed32_count = 5; alltypes.rep_fixed32[4] = 2008; - alltypes.rep_sfixed32_count = 5; alltypes.rep_sfixed32[4] = -2009; - alltypes.rep_float_count = 5; alltypes.rep_float[4] = 2010.0f; - - alltypes.rep_fixed64_count = 5; alltypes.rep_fixed64[4] = 2011; - alltypes.rep_sfixed64_count = 5; alltypes.rep_sfixed64[4] = -2012; - alltypes.rep_double_count = 5; alltypes.rep_double[4] = 2013.0; - - alltypes.rep_string_count = 5; strcpy(alltypes.rep_string[4], "2014"); - alltypes.rep_bytes_count = 5; alltypes.rep_bytes[4].size = 4; - memcpy(alltypes.rep_bytes[4].bytes, "2015", 4); + if (mode == 0 || mode == 1) + { + alltypes.req_int32 = -1001; + alltypes.req_int64 = -1002; + alltypes.req_uint32 = 1003; + alltypes.req_uint64 = 1004; + alltypes.req_sint32 = -1005; + alltypes.req_sint64 = -1006; + alltypes.req_bool = true; + + alltypes.req_fixed32 = 1008; + alltypes.req_sfixed32 = -1009; + alltypes.req_float = 1010.0f; + + alltypes.req_fixed64 = 1011; + alltypes.req_sfixed64 = -1012; + alltypes.req_double = 1013.0; + + strcpy(alltypes.req_string, "1014"); + alltypes.req_bytes.size = 4; + memcpy(alltypes.req_bytes.bytes, "1015", 4); + strcpy(alltypes.req_submsg.substuff1, "1016"); + alltypes.req_submsg.substuff2 = 1016; + alltypes.req_enum = MyEnum_Truth; + memcpy(alltypes.req_fbytes, "1019", 4); + + alltypes.rep_int32_count = 5; alltypes.rep_int32[4] = -2001; + alltypes.rep_int64_count = 5; alltypes.rep_int64[4] = -2002; + alltypes.rep_uint32_count = 5; alltypes.rep_uint32[4] = 2003; + alltypes.rep_uint64_count = 5; alltypes.rep_uint64[4] = 2004; + alltypes.rep_sint32_count = 5; alltypes.rep_sint32[4] = -2005; + alltypes.rep_sint64_count = 5; alltypes.rep_sint64[4] = -2006; + alltypes.rep_bool_count = 5; alltypes.rep_bool[4] = true; + + alltypes.rep_fixed32_count = 5; alltypes.rep_fixed32[4] = 2008; + alltypes.rep_sfixed32_count = 5; alltypes.rep_sfixed32[4] = -2009; + alltypes.rep_float_count = 5; alltypes.rep_float[4] = 2010.0f; + + alltypes.rep_fixed64_count = 5; alltypes.rep_fixed64[4] = 2011; + alltypes.rep_sfixed64_count = 5; alltypes.rep_sfixed64[4] = -2012; + alltypes.rep_double_count = 5; alltypes.rep_double[4] = 2013.0; + + alltypes.rep_string_count = 5; strcpy(alltypes.rep_string[4], "2014"); + alltypes.rep_bytes_count = 5; alltypes.rep_bytes[4].size = 4; + memcpy(alltypes.rep_bytes[4].bytes, "2015", 4); - alltypes.rep_submsg_count = 5; - strcpy(alltypes.rep_submsg[4].substuff1, "2016"); - alltypes.rep_submsg[4].substuff2 = 2016; - alltypes.rep_submsg[4].has_substuff3 = true; - alltypes.rep_submsg[4].substuff3 = 2016; - - alltypes.rep_enum_count = 5; alltypes.rep_enum[4] = MyEnum_Truth; - alltypes.rep_emptymsg_count = 5; - - alltypes.req_limits.int32_min = INT32_MIN; - alltypes.req_limits.int32_max = INT32_MAX; - alltypes.req_limits.uint32_min = 0; - alltypes.req_limits.uint32_max = UINT32_MAX; - alltypes.req_limits.int64_min = INT64_MIN; - alltypes.req_limits.int64_max = INT64_MAX; - alltypes.req_limits.uint64_min = 0; - alltypes.req_limits.uint64_max = UINT64_MAX; - alltypes.req_limits.enum_min = HugeEnum_Negative; - alltypes.req_limits.enum_max = HugeEnum_Positive; + alltypes.rep_submsg_count = 5; + strcpy(alltypes.rep_submsg[4].substuff1, "2016"); + alltypes.rep_submsg[4].substuff2 = 2016; + alltypes.rep_submsg[4].has_substuff3 = true; + alltypes.rep_submsg[4].substuff3 = 2016; + + alltypes.rep_enum_count = 5; alltypes.rep_enum[4] = MyEnum_Truth; + alltypes.rep_emptymsg_count = 5; + + alltypes.rep_fbytes_count = 5; + memcpy(alltypes.rep_fbytes[4], "2019", 4); + + alltypes.req_limits.int32_min = INT32_MIN; + alltypes.req_limits.int32_max = INT32_MAX; + alltypes.req_limits.uint32_min = 0; + alltypes.req_limits.uint32_max = UINT32_MAX; + alltypes.req_limits.int64_min = INT64_MIN; + alltypes.req_limits.int64_max = INT64_MAX; + alltypes.req_limits.uint64_min = 0; + alltypes.req_limits.uint64_max = UINT64_MAX; + alltypes.req_limits.enum_min = HugeEnum_Negative; + alltypes.req_limits.enum_max = HugeEnum_Positive; + } - if (mode != 0) + if (mode == 1) { /* Fill in values for optional fields */ alltypes.has_opt_int32 = true; @@ -121,6 +128,12 @@ int main(int argc, char **argv) alltypes.has_opt_enum = true; alltypes.opt_enum = MyEnum_Truth; alltypes.has_opt_emptymsg = true; + alltypes.has_opt_fbytes = true; + memcpy(alltypes.opt_fbytes, "3059", 4); + + alltypes.which_oneof = AllTypes_oneof_msg1_tag; + strcpy(alltypes.oneof.oneof_msg1.substuff1, "4059"); + alltypes.oneof.oneof_msg1.substuff2 = 4059; } alltypes.end = 1099; diff --git a/tests/alltypes_callback/SConscript b/tests/alltypes_callback/SConscript index 71b0160..8be5390 100644 --- a/tests/alltypes_callback/SConscript +++ b/tests/alltypes_callback/SConscript @@ -1,13 +1,13 @@ # Test the AllTypes encoding & decoding using callbacks for all fields. -Import("env") +Import("env", "malloc_env") c = Copy("$TARGET", "$SOURCE") env.Command("alltypes.proto", "#alltypes/alltypes.proto", c) env.NanopbProto(["alltypes", "alltypes.options"]) -enc = env.Program(["encode_alltypes_callback.c", "alltypes.pb.c", "$COMMON/pb_encode.o"]) -dec = env.Program(["decode_alltypes_callback.c", "alltypes.pb.c", "$COMMON/pb_decode.o"]) +enc = env.Program(["encode_alltypes_callback.c", "alltypes.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) +dec = env.Program(["decode_alltypes_callback.c", "alltypes.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) refdec = "$BUILD/alltypes/decode_alltypes$PROGSUFFIX" @@ -21,3 +21,8 @@ env.RunTest("optionals.output", enc, ARGS = ['1']) env.RunTest("optionals.refdecout", [refdec, "optionals.output"], ARGS = ['1']) env.RunTest("optionals.decout", [dec, "optionals.output"], ARGS = ['1']) +# Try with malloc support also +mallocbin1 = malloc_env.Object("decode_with_malloc.o", "decode_alltypes_callback.c") +mallocbin2 = malloc_env.Object("alltypes_malloc.pb.o", "alltypes.pb.c") +mallocdec = malloc_env.Program("decode_with_malloc", [mallocbin1, mallocbin2, "$COMMON/pb_decode_with_malloc.o", "$COMMON/pb_common_with_malloc.o", "$COMMON/malloc_wrappers.o"]) +env.RunTest("decode_with_malloc.output", [mallocdec, "encode_alltypes_callback.output"]) diff --git a/tests/alltypes_callback/alltypes.options b/tests/alltypes_callback/alltypes.options index a9c55ec..74d7a9c 100644 --- a/tests/alltypes_callback/alltypes.options +++ b/tests/alltypes_callback/alltypes.options @@ -1,3 +1,8 @@ # Generate all fields as callbacks. AllTypes.* type:FT_CALLBACK SubMessage.substuff1 max_size:16 +AllTypes.oneof no_unions:true + +# With FT_CALLBACK, these options should get ignored +*.*fbytes fixed_length:true max_size:4 + diff --git a/tests/alltypes_callback/decode_alltypes_callback.c b/tests/alltypes_callback/decode_alltypes_callback.c index 81274f6..576ce30 100644 --- a/tests/alltypes_callback/decode_alltypes_callback.c +++ b/tests/alltypes_callback/decode_alltypes_callback.c @@ -70,11 +70,15 @@ static bool read_string(pb_istream_t *stream, const pb_field_t *field, void **ar static bool read_submsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { SubMessage submsg = {""}; + SubMessage *ref = *arg; if (!pb_decode(stream, SubMessage_fields, &submsg)) return false; - TEST(memcmp(&submsg, *arg, sizeof(submsg))); + TEST(strcmp(submsg.substuff1, ref->substuff1) == 0); + TEST(submsg.substuff2 == ref->substuff2); + TEST(submsg.has_substuff3 == ref->has_substuff3); + TEST(submsg.substuff3 == ref->substuff3); return true; } @@ -144,11 +148,16 @@ static bool read_repeated_string(pb_istream_t *stream, const pb_field_t *field, static bool read_repeated_submsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { SubMessage** expected = (SubMessage**)arg; - SubMessage decoded = {""}; - if (!pb_decode(stream, SubMessage_fields, &decoded)) + SubMessage submsg = {""}; + if (!pb_decode(stream, SubMessage_fields, &submsg)) return false; - TEST(memcmp((*expected)++, &decoded, sizeof(decoded)) == 0); + TEST(strcmp(submsg.substuff1, (*expected)->substuff1) == 0); + TEST(submsg.substuff2 == (*expected)->substuff2); + TEST(submsg.has_substuff3 == (*expected)->has_substuff3); + TEST(submsg.substuff3 == (*expected)->substuff3); + (*expected)++; + return true; } @@ -177,13 +186,14 @@ static bool read_limits(pb_istream_t *stream, const pb_field_t *field, void **ar bool check_alltypes(pb_istream_t *stream, int mode) { /* Values for use from callbacks through pointers. */ + bool status; uint32_t req_fixed32 = 1008; int32_t req_sfixed32 = -1009; float req_float = 1010.0f; uint64_t req_fixed64 = 1011; int64_t req_sfixed64 = -1012; double req_double = 1013.0; - SubMessage req_submsg = {"1016", 1016}; + SubMessage req_submsg = {"1016", 1016, false, 3}; int32_t rep_int32[5] = {0, 0, 0, 0, -2001}; int32_t rep_int64[5] = {0, 0, 0, 0, -2002}; @@ -213,13 +223,12 @@ bool check_alltypes(pb_istream_t *stream, int mode) uint64_t opt_fixed64 = 3051; int64_t opt_sfixed64 = 3052; double opt_double = 3053.0f; - SubMessage opt_submsg = {"3056", 3056}; + SubMessage opt_submsg = {"3056", 3056, false, 3}; + + SubMessage oneof_msg1 = {"4059", 4059, false, 3}; /* Bind callbacks for required fields */ - AllTypes alltypes; - - /* Fill with garbage to better detect initialization errors */ - memset(&alltypes, 0xAA, sizeof(alltypes)); + AllTypes alltypes = AllTypes_init_zero; alltypes.req_int32.funcs.decode = &read_varint; alltypes.req_int32.arg = (void*)-1001; @@ -391,9 +400,19 @@ bool check_alltypes(pb_istream_t *stream, int mode) alltypes.opt_enum.arg = (void*)MyEnum_Truth; alltypes.opt_emptymsg.funcs.decode = &read_emptymsg; + + alltypes.oneof_msg1.funcs.decode = &read_submsg; + alltypes.oneof_msg1.arg = &oneof_msg1; } - return pb_decode(stream, AllTypes_fields, &alltypes); + status = pb_decode(stream, AllTypes_fields, &alltypes); + +#ifdef PB_ENABLE_MALLOC + /* Just to check for any interference between pb_release() and callback fields */ + pb_release(AllTypes_fields, &alltypes); +#endif + + return status; } int main(int argc, char **argv) diff --git a/tests/alltypes_callback/encode_alltypes_callback.c b/tests/alltypes_callback/encode_alltypes_callback.c index 10560b1..b206783 100644 --- a/tests/alltypes_callback/encode_alltypes_callback.c +++ b/tests/alltypes_callback/encode_alltypes_callback.c @@ -202,6 +202,8 @@ int main(int argc, char **argv) double opt_double = 3053.0f; SubMessage opt_submsg = {"3056", 3056}; + SubMessage oneof_msg1 = {"4059", 4059}; + /* Bind callbacks for required fields */ AllTypes alltypes = {{{0}}}; @@ -261,6 +263,9 @@ int main(int argc, char **argv) alltypes.req_emptymsg.funcs.encode = &write_emptymsg; + alltypes.req_fbytes.funcs.encode = &write_string; + alltypes.req_fbytes.arg = "1019"; + /* Bind callbacks for repeated fields */ alltypes.rep_int32.funcs.encode = &write_repeated_varint; alltypes.rep_int32.arg = (void*)-2001; @@ -315,6 +320,9 @@ int main(int argc, char **argv) alltypes.rep_emptymsg.funcs.encode = &write_repeated_emptymsg; + alltypes.rep_fbytes.funcs.encode = &write_repeated_string; + alltypes.rep_fbytes.arg = "2019"; + alltypes.req_limits.funcs.encode = &write_limits; /* Bind callbacks for optional fields */ @@ -372,6 +380,12 @@ int main(int argc, char **argv) alltypes.opt_enum.arg = (void*)MyEnum_Truth; alltypes.opt_emptymsg.funcs.encode = &write_emptymsg; + + alltypes.opt_fbytes.funcs.encode = &write_string; + alltypes.opt_fbytes.arg = "3059"; + + alltypes.oneof_msg1.funcs.encode = &write_submsg; + alltypes.oneof_msg1.arg = &oneof_msg1; } alltypes.end.funcs.encode = &write_varint; diff --git a/tests/alltypes_pointer/SConscript b/tests/alltypes_pointer/SConscript index e48d6aa..b095ae0 100644 --- a/tests/alltypes_pointer/SConscript +++ b/tests/alltypes_pointer/SConscript @@ -1,30 +1,22 @@ # Encode the AllTypes message using pointers for all fields, and verify the # output against the normal AllTypes test case. -Import("env") - -# We need our own pb_decode.o for the malloc support -env = env.Clone() -env.Append(CPPDEFINES = {'PB_ENABLE_MALLOC': 1}); - -# Disable libmudflap, because it will confuse valgrind -# and other memory leak detection tools. -if '-fmudflap' in env["CCFLAGS"]: - env["CCFLAGS"].remove("-fmudflap") - env["LINKFLAGS"].remove("-fmudflap") - env["LIBS"].remove("mudflap") - -strict = env.Clone() -strict.Append(CFLAGS = strict['CORECFLAGS']) -strict.Object("pb_decode_with_malloc.o", "$NANOPB/pb_decode.c") -strict.Object("pb_encode_with_malloc.o", "$NANOPB/pb_encode.c") +Import("env", "malloc_env") c = Copy("$TARGET", "$SOURCE") env.Command("alltypes.proto", "#alltypes/alltypes.proto", c) env.NanopbProto(["alltypes", "alltypes.options"]) -enc = env.Program(["encode_alltypes_pointer.c", "alltypes.pb.c", "pb_encode_with_malloc.o"]) -dec = env.Program(["decode_alltypes_pointer.c", "alltypes.pb.c", "pb_decode_with_malloc.o"]) +enc = malloc_env.Program(["encode_alltypes_pointer.c", + "alltypes.pb.c", + "$COMMON/pb_encode_with_malloc.o", + "$COMMON/pb_common_with_malloc.o", + "$COMMON/malloc_wrappers.o"]) +dec = malloc_env.Program(["decode_alltypes_pointer.c", + "alltypes.pb.c", + "$COMMON/pb_decode_with_malloc.o", + "$COMMON/pb_common_with_malloc.o", + "$COMMON/malloc_wrappers.o"]) # Encode and compare results to non-pointer alltypes test case env.RunTest(enc) @@ -35,7 +27,7 @@ valgrind = env.WhereIs('valgrind') kwargs = {} if valgrind: kwargs['COMMAND'] = valgrind - kwargs['ARGS'] = ["-q", dec[0].abspath] + kwargs['ARGS'] = ["-q", "--error-exitcode=99", dec[0].abspath] env.RunTest("decode_alltypes.output", [dec, "encode_alltypes_pointer.output"], **kwargs) diff --git a/tests/alltypes_pointer/alltypes.options b/tests/alltypes_pointer/alltypes.options index 52abeb7..8699fe2 100644 --- a/tests/alltypes_pointer/alltypes.options +++ b/tests/alltypes_pointer/alltypes.options @@ -1,3 +1,4 @@ # Generate all fields as pointers. * type:FT_POINTER +*.*fbytes fixed_length:true max_size:4 diff --git a/tests/alltypes_pointer/decode_alltypes_pointer.c b/tests/alltypes_pointer/decode_alltypes_pointer.c index 889676b..4ee6f8b 100644 --- a/tests/alltypes_pointer/decode_alltypes_pointer.c +++ b/tests/alltypes_pointer/decode_alltypes_pointer.c @@ -19,6 +19,7 @@ bool check_alltypes(pb_istream_t *stream, int mode) /* Fill with garbage to better detect initialization errors */ memset(&alltypes, 0xAA, sizeof(alltypes)); + alltypes.extensions = 0; if (!pb_decode(stream, AllTypes_fields, &alltypes)) return false; @@ -46,7 +47,8 @@ bool check_alltypes(pb_istream_t *stream, int mode) && strcmp(alltypes.req_submsg->substuff1, "1016") == 0); TEST(alltypes.req_submsg && alltypes.req_submsg->substuff2 && *alltypes.req_submsg->substuff2 == 1016); - TEST(*alltypes.req_enum == MyEnum_Truth); + TEST(alltypes.req_enum && *alltypes.req_enum == MyEnum_Truth); + TEST(alltypes.req_fbytes && memcmp(alltypes.req_fbytes, "1019", 4) == 0); TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0); TEST(alltypes.rep_int64_count == 5 && alltypes.rep_int64[4] == -2002 && alltypes.rep_int64[0] == 0); @@ -75,6 +77,9 @@ bool check_alltypes(pb_istream_t *stream, int mode) TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero); TEST(alltypes.rep_emptymsg_count == 5); + TEST(alltypes.rep_fbytes_count == 5); + TEST(alltypes.rep_fbytes[0][0] == 0 && alltypes.rep_fbytes[0][3] == 0); + TEST(memcmp(alltypes.rep_fbytes[4], "2019", 4) == 0); if (mode == 0) { @@ -98,6 +103,9 @@ bool check_alltypes(pb_istream_t *stream, int mode) TEST(alltypes.opt_bytes == NULL); TEST(alltypes.opt_submsg == NULL); TEST(alltypes.opt_enum == NULL); + TEST(alltypes.opt_fbytes == NULL); + + TEST(alltypes.which_oneof == 0); } else { @@ -124,6 +132,11 @@ bool check_alltypes(pb_istream_t *stream, int mode) TEST(alltypes.opt_submsg && *alltypes.opt_submsg->substuff2 == 3056); TEST(alltypes.opt_enum && *alltypes.opt_enum == MyEnum_Truth); TEST(alltypes.opt_emptymsg); + TEST(alltypes.opt_fbytes && memcmp(alltypes.opt_fbytes, "3059", 4) == 0); + + TEST(alltypes.which_oneof == AllTypes_oneof_msg1_tag); + TEST(alltypes.oneof.oneof_msg1 && strcmp(alltypes.oneof.oneof_msg1->substuff1, "4059") == 0); + TEST(alltypes.oneof.oneof_msg1->substuff2 && *alltypes.oneof.oneof_msg1->substuff2 == 4059); } TEST(alltypes.req_limits->int32_min && *alltypes.req_limits->int32_min == INT32_MIN); diff --git a/tests/alltypes_pointer/encode_alltypes_pointer.c b/tests/alltypes_pointer/encode_alltypes_pointer.c index c128569..a39af6f 100644 --- a/tests/alltypes_pointer/encode_alltypes_pointer.c +++ b/tests/alltypes_pointer/encode_alltypes_pointer.c @@ -32,6 +32,7 @@ int main(int argc, char **argv) SubMessage req_submsg = {"1016", &req_substuff}; MyEnum req_enum = MyEnum_Truth; EmptyMessage req_emptymsg = {0}; + pb_byte_t req_fbytes[4] = {'1', '0', '1', '9'}; int32_t end = 1099; @@ -62,6 +63,7 @@ int main(int argc, char **argv) {"2016", &rep_substuff2, &rep_substuff3}}; MyEnum rep_enum[5] = {0, 0, 0, 0, MyEnum_Truth}; EmptyMessage rep_emptymsg[5] = {{0}, {0}, {0}, {0}, {0}}; + pb_byte_t rep_fbytes[5][4] = {{0}, {0}, {0}, {0}, {'2', '0', '1', '9'}}; /* Values for optional fields */ int32_t opt_int32 = 3041; @@ -83,6 +85,10 @@ int main(int argc, char **argv) SubMessage opt_submsg = {"3056", &opt_substuff}; MyEnum opt_enum = MyEnum_Truth; EmptyMessage opt_emptymsg = {0}; + pb_byte_t opt_fbytes[4] = {'3', '0', '5', '9'}; + + static int32_t oneof_substuff = 4059; + SubMessage oneof_msg1 = {"4059", &oneof_substuff}; /* Values for the Limits message. */ static int32_t int32_min = INT32_MIN; @@ -122,6 +128,7 @@ int main(int argc, char **argv) alltypes.req_submsg = &req_submsg; alltypes.req_enum = &req_enum; alltypes.req_emptymsg = &req_emptymsg; + alltypes.req_fbytes = &req_fbytes; alltypes.req_limits = &req_limits; alltypes.rep_int32_count = 5; alltypes.rep_int32 = rep_int32; @@ -142,6 +149,7 @@ int main(int argc, char **argv) alltypes.rep_submsg_count = 5; alltypes.rep_submsg = rep_submsg; alltypes.rep_enum_count = 5; alltypes.rep_enum = rep_enum; alltypes.rep_emptymsg_count = 5; alltypes.rep_emptymsg = rep_emptymsg; + alltypes.rep_fbytes_count = 5; alltypes.rep_fbytes = rep_fbytes; if (mode != 0) { @@ -164,6 +172,10 @@ int main(int argc, char **argv) alltypes.opt_submsg = &opt_submsg; alltypes.opt_enum = &opt_enum; alltypes.opt_emptymsg = &opt_emptymsg; + alltypes.opt_fbytes = &opt_fbytes; + + alltypes.which_oneof = AllTypes_oneof_msg1_tag; + alltypes.oneof.oneof_msg1 = &oneof_msg1; } alltypes.end = &end; diff --git a/tests/alltypes_proto3/SConscript b/tests/alltypes_proto3/SConscript new file mode 100644 index 0000000..4c2388e --- /dev/null +++ b/tests/alltypes_proto3/SConscript @@ -0,0 +1,45 @@ +# Version of AllTypes test case for protobuf 3 file format. + +Import("env") + +import re +match = None +if 'PROTOC_VERSION' in env: + match = re.search('([0-9]+).([0-9]+).([0-9]+)', env['PROTOC_VERSION']) + +if match: + version = list(map(int, match.groups())) + +# proto3 syntax is supported by protoc >= 3.0.0 +if env.GetOption('clean') or (match and version[0] >= 3): + + env.NanopbProto(["alltypes", "alltypes.options"]) + enc = env.Program(["encode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) + dec = env.Program(["decode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) + + # Test the round-trip from nanopb encoder to nanopb decoder + env.RunTest(enc) + env.RunTest([dec, "encode_alltypes.output"]) + + # Re-encode the data using protoc, and check that the results from nanopb + # match byte-per-byte to the protoc output. + env.Decode("encode_alltypes.output.decoded", + ["encode_alltypes.output", "alltypes.proto"], + MESSAGE='AllTypes') + env.Encode("encode_alltypes.output.recoded", + ["encode_alltypes.output.decoded", "alltypes.proto"], + MESSAGE='AllTypes') + env.Compare(["encode_alltypes.output", "encode_alltypes.output.recoded"]) + + # Do the same checks with the optional fields present. + env.RunTest("optionals.output", enc, ARGS = ['1']) + env.RunTest("optionals.decout", [dec, "optionals.output"], ARGS = ['1']) + env.Decode("optionals.output.decoded", + ["optionals.output", "alltypes.proto"], + MESSAGE='AllTypes') + env.Encode("optionals.output.recoded", + ["optionals.output.decoded", "alltypes.proto"], + MESSAGE='AllTypes') + env.Compare(["optionals.output", "optionals.output.recoded"]) + + diff --git a/tests/alltypes_proto3/alltypes.options b/tests/alltypes_proto3/alltypes.options new file mode 100644 index 0000000..78dd08d --- /dev/null +++ b/tests/alltypes_proto3/alltypes.options @@ -0,0 +1,4 @@ +* max_size:16 +* max_count:5 +*.*fbytes fixed_length:true max_size:4 + diff --git a/tests/alltypes_proto3/alltypes.proto b/tests/alltypes_proto3/alltypes.proto new file mode 100644 index 0000000..f66109e --- /dev/null +++ b/tests/alltypes_proto3/alltypes.proto @@ -0,0 +1,100 @@ +syntax = "proto3"; +// package name placeholder + +message SubMessage { + string substuff1 = 1; + int32 substuff2 = 2; + fixed32 substuff3 = 3; +} + +message EmptyMessage { + +} + +enum HugeEnum { + HE_Zero = 0; + Negative = -2147483647; /* protoc doesn't accept -2147483648 here */ + Positive = 2147483647; +} + +message Limits { + int32 int32_min = 1; + int32 int32_max = 2; + uint32 uint32_min = 3; + uint32 uint32_max = 4; + int64 int64_min = 5; + int64 int64_max = 6; + uint64 uint64_min = 7; + uint64 uint64_max = 8; + HugeEnum enum_min = 9; + HugeEnum enum_max = 10; +} + +enum MyEnum { + Zero = 0; + First = 1; + Second = 2; + Truth = 42; +} + +message AllTypes { + int32 sng_int32 = 1; + int64 sng_int64 = 2; + uint32 sng_uint32 = 3; + uint64 sng_uint64 = 4; + sint32 sng_sint32 = 5; + sint64 sng_sint64 = 6; + bool sng_bool = 7; + + fixed32 sng_fixed32 = 8; + sfixed32 sng_sfixed32= 9; + float sng_float = 10; + + fixed64 sng_fixed64 = 11; + sfixed64 sng_sfixed64= 12; + double sng_double = 13; + + string sng_string = 14; + bytes sng_bytes = 15; + SubMessage sng_submsg = 16; + MyEnum sng_enum = 17; + EmptyMessage sng_emptymsg = 18; + bytes sng_fbytes = 19; + + repeated int32 rep_int32 = 21 [packed = true]; + repeated int64 rep_int64 = 22 [packed = true]; + repeated uint32 rep_uint32 = 23 [packed = true]; + repeated uint64 rep_uint64 = 24 [packed = true]; + repeated sint32 rep_sint32 = 25 [packed = true]; + repeated sint64 rep_sint64 = 26 [packed = true]; + repeated bool rep_bool = 27 [packed = true]; + + repeated fixed32 rep_fixed32 = 28 [packed = true]; + repeated sfixed32 rep_sfixed32= 29 [packed = true]; + repeated float rep_float = 30 [packed = true]; + + repeated fixed64 rep_fixed64 = 31 [packed = true]; + repeated sfixed64 rep_sfixed64= 32 [packed = true]; + repeated double rep_double = 33 [packed = true]; + + repeated string rep_string = 34; + repeated bytes rep_bytes = 35; + repeated SubMessage rep_submsg = 36; + repeated MyEnum rep_enum = 37 [packed = true]; + repeated EmptyMessage rep_emptymsg = 38; + repeated bytes rep_fbytes = 39; + + oneof oneof + { + SubMessage oneof_msg1 = 59; + EmptyMessage oneof_msg2 = 60; + } + + // Check that extreme integer values are handled correctly + Limits req_limits = 98; + + // Just to make sure that the size of the fields has been calculated + // properly, i.e. otherwise a bug in last field might not be detected. + int32 end = 99; +} + diff --git a/tests/alltypes_proto3/decode_alltypes.c b/tests/alltypes_proto3/decode_alltypes.c new file mode 100644 index 0000000..51c1c41 --- /dev/null +++ b/tests/alltypes_proto3/decode_alltypes.c @@ -0,0 +1,167 @@ +/* Tests the decoding of all types. + * This is the counterpart of test_encode3. + * Run e.g. ./test_encode3 | ./test_decode3 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <pb_decode.h> +#include "alltypes.pb.h" +#include "test_helpers.h" + +#define TEST(x) if (!(x)) { \ + printf("Test " #x " failed.\n"); \ + return false; \ + } + +/* This function is called once from main(), it handles + the decoding and checks the fields. */ +bool check_alltypes(pb_istream_t *stream, int mode) +{ + AllTypes alltypes = AllTypes_init_zero; + + /* Fill with garbage to better detect initialization errors */ + memset(&alltypes, 0xAA, sizeof(alltypes)); + + if (!pb_decode(stream, AllTypes_fields, &alltypes)) + return false; + + TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0); + TEST(alltypes.rep_int64_count == 5 && alltypes.rep_int64[4] == -2002 && alltypes.rep_int64[0] == 0); + TEST(alltypes.rep_uint32_count == 5 && alltypes.rep_uint32[4] == 2003 && alltypes.rep_uint32[0] == 0); + TEST(alltypes.rep_uint64_count == 5 && alltypes.rep_uint64[4] == 2004 && alltypes.rep_uint64[0] == 0); + TEST(alltypes.rep_sint32_count == 5 && alltypes.rep_sint32[4] == -2005 && alltypes.rep_sint32[0] == 0); + TEST(alltypes.rep_sint64_count == 5 && alltypes.rep_sint64[4] == -2006 && alltypes.rep_sint64[0] == 0); + TEST(alltypes.rep_bool_count == 5 && alltypes.rep_bool[4] == true && alltypes.rep_bool[0] == false); + + TEST(alltypes.rep_fixed32_count == 5 && alltypes.rep_fixed32[4] == 2008 && alltypes.rep_fixed32[0] == 0); + TEST(alltypes.rep_sfixed32_count == 5 && alltypes.rep_sfixed32[4] == -2009 && alltypes.rep_sfixed32[0] == 0); + TEST(alltypes.rep_float_count == 5 && alltypes.rep_float[4] == 2010.0f && alltypes.rep_float[0] == 0.0f); + + TEST(alltypes.rep_fixed64_count == 5 && alltypes.rep_fixed64[4] == 2011 && alltypes.rep_fixed64[0] == 0); + TEST(alltypes.rep_sfixed64_count == 5 && alltypes.rep_sfixed64[4] == -2012 && alltypes.rep_sfixed64[0] == 0); + TEST(alltypes.rep_double_count == 5 && alltypes.rep_double[4] == 2013.0 && alltypes.rep_double[0] == 0.0); + + TEST(alltypes.rep_string_count == 5 && strcmp(alltypes.rep_string[4], "2014") == 0 && alltypes.rep_string[0][0] == '\0'); + TEST(alltypes.rep_bytes_count == 5 && alltypes.rep_bytes[4].size == 4 && alltypes.rep_bytes[0].size == 0); + TEST(memcmp(alltypes.rep_bytes[4].bytes, "2015", 4) == 0); + + TEST(alltypes.rep_submsg_count == 5); + TEST(strcmp(alltypes.rep_submsg[4].substuff1, "2016") == 0 && alltypes.rep_submsg[0].substuff1[0] == '\0'); + TEST(alltypes.rep_submsg[4].substuff2 == 2016 && alltypes.rep_submsg[0].substuff2 == 0); + TEST(alltypes.rep_submsg[4].substuff3 == 2016 && alltypes.rep_submsg[0].substuff3 == 0); + + TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero); + TEST(alltypes.rep_emptymsg_count == 5); + + TEST(alltypes.rep_fbytes_count == 5); + TEST(alltypes.rep_fbytes[0][0] == 0 && alltypes.rep_fbytes[0][3] == 0); + TEST(memcmp(alltypes.rep_fbytes[4], "2019", 4) == 0); + + if (mode == 0) + { + /* Expect default values */ + TEST(alltypes.sng_int32 == 0); + TEST(alltypes.sng_int64 == 0); + TEST(alltypes.sng_uint32 == 0); + TEST(alltypes.sng_uint64 == 0); + TEST(alltypes.sng_sint32 == 0); + TEST(alltypes.sng_sint64 == 0); + TEST(alltypes.sng_bool == false); + + TEST(alltypes.sng_fixed32 == 0); + TEST(alltypes.sng_sfixed32 == 0); + TEST(alltypes.sng_float == 0.0f); + + TEST(alltypes.sng_fixed64 == 0); + TEST(alltypes.sng_sfixed64 == 0); + TEST(alltypes.sng_double == 0.0); + + TEST(strcmp(alltypes.sng_string, "") == 0); + TEST(alltypes.sng_bytes.size == 0); + TEST(strcmp(alltypes.sng_submsg.substuff1, "") == 0); + TEST(alltypes.sng_submsg.substuff2 == 0); + TEST(alltypes.sng_submsg.substuff3 == 0); + TEST(alltypes.sng_enum == MyEnum_Zero); + TEST(alltypes.sng_fbytes[0] == 0 && + alltypes.sng_fbytes[1] == 0 && + alltypes.sng_fbytes[2] == 0 && + alltypes.sng_fbytes[3] == 0); + + TEST(alltypes.which_oneof == 0); + } + else + { + /* Expect filled-in values */ + TEST(alltypes.sng_int32 == 3041); + TEST(alltypes.sng_int64 == 3042); + TEST(alltypes.sng_uint32 == 3043); + TEST(alltypes.sng_uint64 == 3044); + TEST(alltypes.sng_sint32 == 3045); + TEST(alltypes.sng_sint64 == 3046); + TEST(alltypes.sng_bool == true); + + TEST(alltypes.sng_fixed32 == 3048); + TEST(alltypes.sng_sfixed32 == 3049); + TEST(alltypes.sng_float == 3050.0f); + + TEST(alltypes.sng_fixed64 == 3051); + TEST(alltypes.sng_sfixed64 == 3052); + TEST(alltypes.sng_double == 3053.0); + + TEST(strcmp(alltypes.sng_string, "3054") == 0); + TEST(alltypes.sng_bytes.size == 4); + TEST(memcmp(alltypes.sng_bytes.bytes, "3055", 4) == 0); + TEST(strcmp(alltypes.sng_submsg.substuff1, "3056") == 0); + TEST(alltypes.sng_submsg.substuff2 == 3056); + TEST(alltypes.sng_submsg.substuff3 == 0); + TEST(alltypes.sng_enum == MyEnum_Truth); + TEST(memcmp(alltypes.sng_fbytes, "3059", 4) == 0); + + TEST(alltypes.which_oneof == AllTypes_oneof_msg1_tag); + TEST(strcmp(alltypes.oneof.oneof_msg1.substuff1, "4059") == 0); + TEST(alltypes.oneof.oneof_msg1.substuff2 == 4059); + } + + TEST(alltypes.req_limits.int32_min == INT32_MIN); + TEST(alltypes.req_limits.int32_max == INT32_MAX); + TEST(alltypes.req_limits.uint32_min == 0); + TEST(alltypes.req_limits.uint32_max == UINT32_MAX); + TEST(alltypes.req_limits.int64_min == INT64_MIN); + TEST(alltypes.req_limits.int64_max == INT64_MAX); + TEST(alltypes.req_limits.uint64_min == 0); + TEST(alltypes.req_limits.uint64_max == UINT64_MAX); + TEST(alltypes.req_limits.enum_min == HugeEnum_Negative); + TEST(alltypes.req_limits.enum_max == HugeEnum_Positive); + + TEST(alltypes.end == 1099); + + return true; +} + +int main(int argc, char **argv) +{ + uint8_t buffer[1024]; + size_t count; + pb_istream_t stream; + + /* Whether to expect the optional values or the default values. */ + int mode = (argc > 1) ? atoi(argv[1]) : 0; + + /* Read the data into buffer */ + SET_BINARY_MODE(stdin); + count = fread(buffer, 1, sizeof(buffer), stdin); + + /* Construct a pb_istream_t for reading from the buffer */ + stream = pb_istream_from_buffer(buffer, count); + + /* Decode and print out the stuff */ + if (!check_alltypes(&stream, mode)) + { + printf("Parsing failed: %s\n", PB_GET_ERROR(&stream)); + return 1; + } else { + return 0; + } +} diff --git a/tests/alltypes_proto3/encode_alltypes.c b/tests/alltypes_proto3/encode_alltypes.c new file mode 100644 index 0000000..1da0668 --- /dev/null +++ b/tests/alltypes_proto3/encode_alltypes.c @@ -0,0 +1,111 @@ +/* Attempts to test all the datatypes supported by ProtoBuf3. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pb_encode.h> +#include "alltypes.pb.h" +#include "test_helpers.h" + +int main(int argc, char **argv) +{ + int mode = (argc > 1) ? atoi(argv[1]) : 0; + + /* Initialize the structure with constants */ + AllTypes alltypes = AllTypes_init_zero; + + alltypes.rep_int32_count = 5; alltypes.rep_int32[4] = -2001; + alltypes.rep_int64_count = 5; alltypes.rep_int64[4] = -2002; + alltypes.rep_uint32_count = 5; alltypes.rep_uint32[4] = 2003; + alltypes.rep_uint64_count = 5; alltypes.rep_uint64[4] = 2004; + alltypes.rep_sint32_count = 5; alltypes.rep_sint32[4] = -2005; + alltypes.rep_sint64_count = 5; alltypes.rep_sint64[4] = -2006; + alltypes.rep_bool_count = 5; alltypes.rep_bool[4] = true; + + alltypes.rep_fixed32_count = 5; alltypes.rep_fixed32[4] = 2008; + alltypes.rep_sfixed32_count = 5; alltypes.rep_sfixed32[4] = -2009; + alltypes.rep_float_count = 5; alltypes.rep_float[4] = 2010.0f; + + alltypes.rep_fixed64_count = 5; alltypes.rep_fixed64[4] = 2011; + alltypes.rep_sfixed64_count = 5; alltypes.rep_sfixed64[4] = -2012; + alltypes.rep_double_count = 5; alltypes.rep_double[4] = 2013.0; + + alltypes.rep_string_count = 5; strcpy(alltypes.rep_string[4], "2014"); + alltypes.rep_bytes_count = 5; alltypes.rep_bytes[4].size = 4; + memcpy(alltypes.rep_bytes[4].bytes, "2015", 4); + + alltypes.rep_submsg_count = 5; + strcpy(alltypes.rep_submsg[4].substuff1, "2016"); + alltypes.rep_submsg[4].substuff2 = 2016; + alltypes.rep_submsg[4].substuff3 = 2016; + + alltypes.rep_enum_count = 5; alltypes.rep_enum[4] = MyEnum_Truth; + alltypes.rep_emptymsg_count = 5; + + alltypes.rep_fbytes_count = 5; + memcpy(alltypes.rep_fbytes[4], "2019", 4); + + alltypes.req_limits.int32_min = INT32_MIN; + alltypes.req_limits.int32_max = INT32_MAX; + alltypes.req_limits.uint32_min = 0; + alltypes.req_limits.uint32_max = UINT32_MAX; + alltypes.req_limits.int64_min = INT64_MIN; + alltypes.req_limits.int64_max = INT64_MAX; + alltypes.req_limits.uint64_min = 0; + alltypes.req_limits.uint64_max = UINT64_MAX; + alltypes.req_limits.enum_min = HugeEnum_Negative; + alltypes.req_limits.enum_max = HugeEnum_Positive; + + if (mode != 0) + { + /* Fill in values for singular fields */ + alltypes.sng_int32 = 3041; + alltypes.sng_int64 = 3042; + alltypes.sng_uint32 = 3043; + alltypes.sng_uint64 = 3044; + alltypes.sng_sint32 = 3045; + alltypes.sng_sint64 = 3046; + alltypes.sng_bool = true; + + alltypes.sng_fixed32 = 3048; + alltypes.sng_sfixed32 = 3049; + alltypes.sng_float = 3050.0f; + + alltypes.sng_fixed64 = 3051; + alltypes.sng_sfixed64 = 3052; + alltypes.sng_double = 3053.0; + + strcpy(alltypes.sng_string, "3054"); + alltypes.sng_bytes.size = 4; + memcpy(alltypes.sng_bytes.bytes, "3055", 4); + strcpy(alltypes.sng_submsg.substuff1, "3056"); + alltypes.sng_submsg.substuff2 = 3056; + alltypes.sng_enum = MyEnum_Truth; + memcpy(alltypes.sng_fbytes, "3059", 4); + + alltypes.which_oneof = AllTypes_oneof_msg1_tag; + strcpy(alltypes.oneof.oneof_msg1.substuff1, "4059"); + alltypes.oneof.oneof_msg1.substuff2 = 4059; + } + + alltypes.end = 1099; + + { + uint8_t buffer[AllTypes_size]; + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + + /* Now encode it and check if we succeeded. */ + if (pb_encode(&stream, AllTypes_fields, &alltypes)) + { + SET_BINARY_MODE(stdout); + fwrite(buffer, 1, stream.bytes_written, stdout); + return 0; /* Success */ + } + else + { + fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream)); + return 1; /* Failure */ + } + } +} diff --git a/tests/alltypes_proto3_callback/SConscript b/tests/alltypes_proto3_callback/SConscript new file mode 100644 index 0000000..183a138 --- /dev/null +++ b/tests/alltypes_proto3_callback/SConscript @@ -0,0 +1,23 @@ +# Test the AllTypes encoding & decoding using callbacks for all fields. + +Import("env", "malloc_env") + +c = Copy("$TARGET", "$SOURCE") +env.Command("alltypes.proto", "#alltypes_proto3/alltypes.proto", c) + +env.NanopbProto(["alltypes", "alltypes.options"]) +enc = env.Program(["encode_alltypes_callback.c", "alltypes.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) +dec = env.Program(["decode_alltypes_callback.c", "alltypes.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) + +refdec = "$BUILD/alltypes_proto3/decode_alltypes$PROGSUFFIX" + +# Encode and compare results +env.RunTest(enc) +env.RunTest("decode_alltypes.output", [refdec, "encode_alltypes_callback.output"]) +env.RunTest("decode_alltypes_callback.output", [dec, "encode_alltypes_callback.output"]) + +# Do the same thing with the optional fields present +env.RunTest("optionals.output", enc, ARGS = ['1']) +env.RunTest("optionals.refdecout", [refdec, "optionals.output"], ARGS = ['1']) +env.RunTest("optionals.decout", [dec, "optionals.output"], ARGS = ['1']) + diff --git a/tests/alltypes_proto3_callback/alltypes.options b/tests/alltypes_proto3_callback/alltypes.options new file mode 100644 index 0000000..74d7a9c --- /dev/null +++ b/tests/alltypes_proto3_callback/alltypes.options @@ -0,0 +1,8 @@ +# Generate all fields as callbacks. +AllTypes.* type:FT_CALLBACK +SubMessage.substuff1 max_size:16 +AllTypes.oneof no_unions:true + +# With FT_CALLBACK, these options should get ignored +*.*fbytes fixed_length:true max_size:4 + diff --git a/tests/alltypes_proto3_callback/decode_alltypes_callback.c b/tests/alltypes_proto3_callback/decode_alltypes_callback.c new file mode 100644 index 0000000..2b3c2f3 --- /dev/null +++ b/tests/alltypes_proto3_callback/decode_alltypes_callback.c @@ -0,0 +1,376 @@ +/* Attempts to test all the datatypes supported by ProtoBuf when used as callback fields. + * Note that normally there would be no reason to use callback fields for this, + * because each encoder defined here only gives a single field. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <pb_decode.h> +#include "alltypes.pb.h" +#include "test_helpers.h" + +#define TEST(x) if (!(x)) { \ + printf("Test " #x " failed (in field %d).\n", field->tag); \ + return false; \ + } + +static bool read_varint(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + TEST((int64_t)value == (long)*arg); + return true; +} + +static bool read_svarint(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + int64_t value; + if (!pb_decode_svarint(stream, &value)) + return false; + + TEST(value == (long)*arg); + return true; +} + +static bool read_fixed32(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + uint32_t value; + if (!pb_decode_fixed32(stream, &value)) + return false; + + TEST(value == *(uint32_t*)*arg); + return true; +} + +static bool read_fixed64(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + uint64_t value; + if (!pb_decode_fixed64(stream, &value)) + return false; + + TEST(value == *(uint64_t*)*arg); + return true; +} + +static bool read_string(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + uint8_t buf[16] = {0}; + size_t len = stream->bytes_left; + + if (len > sizeof(buf) - 1 || !pb_read(stream, buf, len)) + return false; + + TEST(strcmp((char*)buf, *arg) == 0); + return true; +} + +static bool read_submsg(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + SubMessage submsg = {""}; + SubMessage *ref = *arg; + + if (!pb_decode(stream, SubMessage_fields, &submsg)) + return false; + + TEST(strcmp(submsg.substuff1, ref->substuff1) == 0); + TEST(submsg.substuff2 == ref->substuff2); + TEST(submsg.substuff3 == ref->substuff3); + return true; +} + +static bool read_emptymsg(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + EmptyMessage emptymsg = {0}; + return pb_decode(stream, EmptyMessage_fields, &emptymsg); +} + +static bool read_repeated_varint(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + int32_t** expected = (int32_t**)arg; + uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + TEST(*(*expected)++ == value); + return true; +} + +static bool read_repeated_svarint(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + int32_t** expected = (int32_t**)arg; + int64_t value; + if (!pb_decode_svarint(stream, &value)) + return false; + + TEST(*(*expected)++ == value); + return true; +} + +static bool read_repeated_fixed32(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + uint32_t** expected = (uint32_t**)arg; + uint32_t value; + if (!pb_decode_fixed32(stream, &value)) + return false; + + TEST(*(*expected)++ == value); + return true; +} + +static bool read_repeated_fixed64(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + uint64_t** expected = (uint64_t**)arg; + uint64_t value; + if (!pb_decode_fixed64(stream, &value)) + return false; + + TEST(*(*expected)++ == value); + return true; +} + +static bool read_repeated_string(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + uint8_t*** expected = (uint8_t***)arg; + uint8_t buf[16] = {0}; + size_t len = stream->bytes_left; + + if (len > sizeof(buf) - 1 || !pb_read(stream, buf, len)) + return false; + + TEST(strcmp((char*)*(*expected)++, (char*)buf) == 0); + return true; +} + +static bool read_repeated_submsg(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + SubMessage** expected = (SubMessage**)arg; + SubMessage submsg = {""}; + if (!pb_decode(stream, SubMessage_fields, &submsg)) + return false; + + TEST(strcmp(submsg.substuff1, (*expected)->substuff1) == 0); + TEST(submsg.substuff2 == (*expected)->substuff2); + TEST(submsg.substuff3 == (*expected)->substuff3); + (*expected)++; + + return true; +} + +static bool read_limits(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + Limits decoded = {0}; + if (!pb_decode(stream, Limits_fields, &decoded)) + return false; + + TEST(decoded.int32_min == INT32_MIN); + TEST(decoded.int32_max == INT32_MAX); + TEST(decoded.uint32_min == 0); + TEST(decoded.uint32_max == UINT32_MAX); + TEST(decoded.int64_min == INT64_MIN); + TEST(decoded.int64_max == INT64_MAX); + TEST(decoded.uint64_min == 0); + TEST(decoded.uint64_max == UINT64_MAX); + TEST(decoded.enum_min == HugeEnum_Negative); + TEST(decoded.enum_max == HugeEnum_Positive); + + return true; +} + +/* This function is called once from main(), it handles + the decoding and checks the fields. */ +bool check_alltypes(pb_istream_t *stream, int mode) +{ + /* Values for use from callbacks through pointers. */ + bool status; + + int32_t rep_int32[5] = {0, 0, 0, 0, -2001}; + int32_t rep_int64[5] = {0, 0, 0, 0, -2002}; + int32_t rep_uint32[5] = {0, 0, 0, 0, 2003}; + int32_t rep_uint64[5] = {0, 0, 0, 0, 2004}; + int32_t rep_sint32[5] = {0, 0, 0, 0, -2005}; + int32_t rep_sint64[5] = {0, 0, 0, 0, -2006}; + int32_t rep_bool[5] = {false, false, false, false, true}; + uint32_t rep_fixed32[5] = {0, 0, 0, 0, 2008}; + int32_t rep_sfixed32[5] = {0, 0, 0, 0, -2009}; + float rep_float[5] = {0, 0, 0, 0, 2010.0f}; + uint64_t rep_fixed64[5] = {0, 0, 0, 0, 2011}; + int64_t rep_sfixed64[5] = {0, 0, 0, 0, -2012}; + double rep_double[5] = {0, 0, 0, 0, 2013.0}; + char* rep_string[5] = {"", "", "", "", "2014"}; + char* rep_bytes[5] = {"", "", "", "", "2015"}; + SubMessage rep_submsg[5] = {{"", 0, 0}, + {"", 0, 0}, + {"", 0, 0}, + {"", 0, 0}, + {"2016", 2016, 2016}}; + int32_t rep_enum[5] = {0, 0, 0, 0, MyEnum_Truth}; + + uint32_t sng_fixed32 = 3048; + int32_t sng_sfixed32 = 3049; + float sng_float = 3050.0f; + uint64_t sng_fixed64 = 3051; + int64_t sng_sfixed64 = 3052; + double sng_double = 3053.0f; + SubMessage sng_submsg = {"3056", 3056}; + + SubMessage oneof_msg1 = {"4059", 4059}; + + AllTypes alltypes = AllTypes_init_zero; + + /* Bind callbacks for repeated fields */ + alltypes.rep_int32.funcs.decode = &read_repeated_varint; + alltypes.rep_int32.arg = rep_int32; + + alltypes.rep_int64.funcs.decode = &read_repeated_varint; + alltypes.rep_int64.arg = rep_int64; + + alltypes.rep_uint32.funcs.decode = &read_repeated_varint; + alltypes.rep_uint32.arg = rep_uint32; + + alltypes.rep_uint64.funcs.decode = &read_repeated_varint; + alltypes.rep_uint64.arg = rep_uint64; + + alltypes.rep_sint32.funcs.decode = &read_repeated_svarint; + alltypes.rep_sint32.arg = rep_sint32; + + alltypes.rep_sint64.funcs.decode = &read_repeated_svarint; + alltypes.rep_sint64.arg = rep_sint64; + + alltypes.rep_bool.funcs.decode = &read_repeated_varint; + alltypes.rep_bool.arg = rep_bool; + + alltypes.rep_fixed32.funcs.decode = &read_repeated_fixed32; + alltypes.rep_fixed32.arg = rep_fixed32; + + alltypes.rep_sfixed32.funcs.decode = &read_repeated_fixed32; + alltypes.rep_sfixed32.arg = rep_sfixed32; + + alltypes.rep_float.funcs.decode = &read_repeated_fixed32; + alltypes.rep_float.arg = rep_float; + + alltypes.rep_fixed64.funcs.decode = &read_repeated_fixed64; + alltypes.rep_fixed64.arg = rep_fixed64; + + alltypes.rep_sfixed64.funcs.decode = &read_repeated_fixed64; + alltypes.rep_sfixed64.arg = rep_sfixed64; + + alltypes.rep_double.funcs.decode = &read_repeated_fixed64; + alltypes.rep_double.arg = rep_double; + + alltypes.rep_string.funcs.decode = &read_repeated_string; + alltypes.rep_string.arg = rep_string; + + alltypes.rep_bytes.funcs.decode = &read_repeated_string; + alltypes.rep_bytes.arg = rep_bytes; + + alltypes.rep_submsg.funcs.decode = &read_repeated_submsg; + alltypes.rep_submsg.arg = rep_submsg; + + alltypes.rep_enum.funcs.decode = &read_repeated_varint; + alltypes.rep_enum.arg = rep_enum; + + alltypes.rep_emptymsg.funcs.decode = &read_emptymsg; + + alltypes.req_limits.funcs.decode = &read_limits; + + alltypes.end.funcs.decode = &read_varint; + alltypes.end.arg = (void*)1099; + + /* Bind callbacks for optional fields */ + if (mode == 1) + { + alltypes.sng_int32.funcs.decode = &read_varint; + alltypes.sng_int32.arg = (void*)3041; + + alltypes.sng_int64.funcs.decode = &read_varint; + alltypes.sng_int64.arg = (void*)3042; + + alltypes.sng_uint32.funcs.decode = &read_varint; + alltypes.sng_uint32.arg = (void*)3043; + + alltypes.sng_uint64.funcs.decode = &read_varint; + alltypes.sng_uint64.arg = (void*)3044; + + alltypes.sng_sint32.funcs.decode = &read_svarint; + alltypes.sng_sint32.arg = (void*)3045; + + alltypes.sng_sint64.funcs.decode = &read_svarint; + alltypes.sng_sint64.arg = (void*)3046; + + alltypes.sng_bool.funcs.decode = &read_varint; + alltypes.sng_bool.arg = (void*)true; + + alltypes.sng_fixed32.funcs.decode = &read_fixed32; + alltypes.sng_fixed32.arg = &sng_fixed32; + + alltypes.sng_sfixed32.funcs.decode = &read_fixed32; + alltypes.sng_sfixed32.arg = &sng_sfixed32; + + alltypes.sng_float.funcs.decode = &read_fixed32; + alltypes.sng_float.arg = &sng_float; + + alltypes.sng_fixed64.funcs.decode = &read_fixed64; + alltypes.sng_fixed64.arg = &sng_fixed64; + + alltypes.sng_sfixed64.funcs.decode = &read_fixed64; + alltypes.sng_sfixed64.arg = &sng_sfixed64; + + alltypes.sng_double.funcs.decode = &read_fixed64; + alltypes.sng_double.arg = &sng_double; + + alltypes.sng_string.funcs.decode = &read_string; + alltypes.sng_string.arg = "3054"; + + alltypes.sng_bytes.funcs.decode = &read_string; + alltypes.sng_bytes.arg = "3055"; + + alltypes.sng_submsg.funcs.decode = &read_submsg; + alltypes.sng_submsg.arg = &sng_submsg; + + alltypes.sng_enum.funcs.decode = &read_varint; + alltypes.sng_enum.arg = (void*)MyEnum_Truth; + + alltypes.sng_emptymsg.funcs.decode = &read_emptymsg; + + alltypes.oneof_msg1.funcs.decode = &read_submsg; + alltypes.oneof_msg1.arg = &oneof_msg1; + } + + status = pb_decode(stream, AllTypes_fields, &alltypes); + +#ifdef PB_ENABLE_MALLOC + /* Just to check for any interference between pb_release() and callback fields */ + pb_release(AllTypes_fields, &alltypes); +#endif + + return status; +} + +int main(int argc, char **argv) +{ + uint8_t buffer[1024]; + size_t count; + pb_istream_t stream; + + /* Whether to expect the optional values or the default values. */ + int mode = (argc > 1) ? atoi(argv[1]) : 0; + + /* Read the data into buffer */ + SET_BINARY_MODE(stdin); + count = fread(buffer, 1, sizeof(buffer), stdin); + + /* Construct a pb_istream_t for reading from the buffer */ + stream = pb_istream_from_buffer(buffer, count); + + /* Decode and print out the stuff */ + if (!check_alltypes(&stream, mode)) + { + printf("Parsing failed: %s\n", PB_GET_ERROR(&stream)); + return 1; + } else { + return 0; + } +} diff --git a/tests/alltypes_proto3_callback/encode_alltypes_callback.c b/tests/alltypes_proto3_callback/encode_alltypes_callback.c new file mode 100644 index 0000000..8c7bdd6 --- /dev/null +++ b/tests/alltypes_proto3_callback/encode_alltypes_callback.c @@ -0,0 +1,343 @@ +/* Attempts to test all the datatypes supported by ProtoBuf when used as callback fields. + * Note that normally there would be no reason to use callback fields for this, + * because each encoder defined here only gives a single field. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pb_encode.h> +#include "alltypes.pb.h" +#include "test_helpers.h" + +static bool write_varint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + return pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, (long)*arg); +} + +static bool write_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + return pb_encode_tag_for_field(stream, field) && + pb_encode_svarint(stream, (long)*arg); +} + +static bool write_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + return pb_encode_tag_for_field(stream, field) && + pb_encode_fixed32(stream, *arg); +} + +static bool write_fixed64(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + return pb_encode_tag_for_field(stream, field) && + pb_encode_fixed64(stream, *arg); +} + +static bool write_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + return pb_encode_tag_for_field(stream, field) && + pb_encode_string(stream, *arg, strlen(*arg)); +} + +static bool write_submsg(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + + return pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, SubMessage_fields, *arg); +} + +static bool write_emptymsg(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + EmptyMessage emptymsg = {0}; + return pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg); +} + +static bool write_repeated_varint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + return pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, (long)*arg); +} + +static bool write_repeated_svarint(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + return pb_encode_tag_for_field(stream, field) && + pb_encode_svarint(stream, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_svarint(stream, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_svarint(stream, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_svarint(stream, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_svarint(stream, (long)*arg); +} + +static bool write_repeated_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + uint32_t dummy = 0; + + /* Make it a packed field */ + return pb_encode_tag(stream, PB_WT_STRING, field->tag) && + pb_encode_varint(stream, 5 * 4) && /* Number of bytes */ + pb_encode_fixed32(stream, &dummy) && + pb_encode_fixed32(stream, &dummy) && + pb_encode_fixed32(stream, &dummy) && + pb_encode_fixed32(stream, &dummy) && + pb_encode_fixed32(stream, *arg); +} + +static bool write_repeated_fixed64(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + uint64_t dummy = 0; + + /* Make it a packed field */ + return pb_encode_tag(stream, PB_WT_STRING, field->tag) && + pb_encode_varint(stream, 5 * 8) && /* Number of bytes */ + pb_encode_fixed64(stream, &dummy) && + pb_encode_fixed64(stream, &dummy) && + pb_encode_fixed64(stream, &dummy) && + pb_encode_fixed64(stream, &dummy) && + pb_encode_fixed64(stream, *arg); +} + +static bool write_repeated_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + return pb_encode_tag_for_field(stream, field) && + pb_encode_string(stream, 0, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_string(stream, 0, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_string(stream, 0, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_string(stream, 0, 0) && + pb_encode_tag_for_field(stream, field) && + pb_encode_string(stream, *arg, strlen(*arg)); +} + +static bool write_repeated_submsg(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + SubMessage dummy = {""}; + + return pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, SubMessage_fields, &dummy) && + pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, SubMessage_fields, &dummy) && + pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, SubMessage_fields, &dummy) && + pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, SubMessage_fields, &dummy) && + pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, SubMessage_fields, *arg); +} + +static bool write_limits(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + Limits limits = {0}; + limits.int32_min = INT32_MIN; + limits.int32_max = INT32_MAX; + limits.uint32_min = 0; + limits.uint32_max = UINT32_MAX; + limits.int64_min = INT64_MIN; + limits.int64_max = INT64_MAX; + limits.uint64_min = 0; + limits.uint64_max = UINT64_MAX; + limits.enum_min = HugeEnum_Negative; + limits.enum_max = HugeEnum_Positive; + + return pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, Limits_fields, &limits); +} + +static bool write_repeated_emptymsg(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + EmptyMessage emptymsg = {0}; + return pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg) && + pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg) && + pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg) && + pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg) && + pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, EmptyMessage_fields, &emptymsg); +} + +int main(int argc, char **argv) +{ + int mode = (argc > 1) ? atoi(argv[1]) : 0; + + /* Values for use from callbacks through pointers. */ + uint32_t rep_fixed32 = 2008; + int32_t rep_sfixed32 = -2009; + float rep_float = 2010.0f; + uint64_t rep_fixed64 = 2011; + int64_t rep_sfixed64 = -2012; + double rep_double = 2013.0; + SubMessage rep_submsg = {"2016", 2016, 2016}; + + uint32_t sng_fixed32 = 3048; + int32_t sng_sfixed32 = 3049; + float sng_float = 3050.0f; + uint64_t sng_fixed64 = 3051; + int64_t sng_sfixed64 = 3052; + double sng_double = 3053.0f; + SubMessage sng_submsg = {"3056", 3056}; + + SubMessage oneof_msg1 = {"4059", 4059}; + + AllTypes alltypes = AllTypes_init_zero; + + /* Bind callbacks for repeated fields */ + alltypes.rep_int32.funcs.encode = &write_repeated_varint; + alltypes.rep_int32.arg = (void*)-2001; + + alltypes.rep_int64.funcs.encode = &write_repeated_varint; + alltypes.rep_int64.arg = (void*)-2002; + + alltypes.rep_uint32.funcs.encode = &write_repeated_varint; + alltypes.rep_uint32.arg = (void*)2003; + + alltypes.rep_uint64.funcs.encode = &write_repeated_varint; + alltypes.rep_uint64.arg = (void*)2004; + + alltypes.rep_sint32.funcs.encode = &write_repeated_svarint; + alltypes.rep_sint32.arg = (void*)-2005; + + alltypes.rep_sint64.funcs.encode = &write_repeated_svarint; + alltypes.rep_sint64.arg = (void*)-2006; + + alltypes.rep_bool.funcs.encode = &write_repeated_varint; + alltypes.rep_bool.arg = (void*)true; + + alltypes.rep_fixed32.funcs.encode = &write_repeated_fixed32; + alltypes.rep_fixed32.arg = &rep_fixed32; + + alltypes.rep_sfixed32.funcs.encode = &write_repeated_fixed32; + alltypes.rep_sfixed32.arg = &rep_sfixed32; + + alltypes.rep_float.funcs.encode = &write_repeated_fixed32; + alltypes.rep_float.arg = &rep_float; + + alltypes.rep_fixed64.funcs.encode = &write_repeated_fixed64; + alltypes.rep_fixed64.arg = &rep_fixed64; + + alltypes.rep_sfixed64.funcs.encode = &write_repeated_fixed64; + alltypes.rep_sfixed64.arg = &rep_sfixed64; + + alltypes.rep_double.funcs.encode = &write_repeated_fixed64; + alltypes.rep_double.arg = &rep_double; + + alltypes.rep_string.funcs.encode = &write_repeated_string; + alltypes.rep_string.arg = "2014"; + + alltypes.rep_bytes.funcs.encode = &write_repeated_string; + alltypes.rep_bytes.arg = "2015"; + + alltypes.rep_submsg.funcs.encode = &write_repeated_submsg; + alltypes.rep_submsg.arg = &rep_submsg; + + alltypes.rep_enum.funcs.encode = &write_repeated_varint; + alltypes.rep_enum.arg = (void*)MyEnum_Truth; + + alltypes.rep_emptymsg.funcs.encode = &write_repeated_emptymsg; + + alltypes.rep_fbytes.funcs.encode = &write_repeated_string; + alltypes.rep_fbytes.arg = "2019"; + + alltypes.req_limits.funcs.encode = &write_limits; + + /* Bind callbacks for singular fields */ + if (mode != 0) + { + alltypes.sng_int32.funcs.encode = &write_varint; + alltypes.sng_int32.arg = (void*)3041; + + alltypes.sng_int64.funcs.encode = &write_varint; + alltypes.sng_int64.arg = (void*)3042; + + alltypes.sng_uint32.funcs.encode = &write_varint; + alltypes.sng_uint32.arg = (void*)3043; + + alltypes.sng_uint64.funcs.encode = &write_varint; + alltypes.sng_uint64.arg = (void*)3044; + + alltypes.sng_sint32.funcs.encode = &write_svarint; + alltypes.sng_sint32.arg = (void*)3045; + + alltypes.sng_sint64.funcs.encode = &write_svarint; + alltypes.sng_sint64.arg = (void*)3046; + + alltypes.sng_bool.funcs.encode = &write_varint; + alltypes.sng_bool.arg = (void*)true; + + alltypes.sng_fixed32.funcs.encode = &write_fixed32; + alltypes.sng_fixed32.arg = &sng_fixed32; + + alltypes.sng_sfixed32.funcs.encode = &write_fixed32; + alltypes.sng_sfixed32.arg = &sng_sfixed32; + + alltypes.sng_float.funcs.encode = &write_fixed32; + alltypes.sng_float.arg = &sng_float; + + alltypes.sng_fixed64.funcs.encode = &write_fixed64; + alltypes.sng_fixed64.arg = &sng_fixed64; + + alltypes.sng_sfixed64.funcs.encode = &write_fixed64; + alltypes.sng_sfixed64.arg = &sng_sfixed64; + + alltypes.sng_double.funcs.encode = &write_fixed64; + alltypes.sng_double.arg = &sng_double; + + alltypes.sng_string.funcs.encode = &write_string; + alltypes.sng_string.arg = "3054"; + + alltypes.sng_bytes.funcs.encode = &write_string; + alltypes.sng_bytes.arg = "3055"; + + alltypes.sng_submsg.funcs.encode = &write_submsg; + alltypes.sng_submsg.arg = &sng_submsg; + + alltypes.sng_enum.funcs.encode = &write_varint; + alltypes.sng_enum.arg = (void*)MyEnum_Truth; + + alltypes.sng_emptymsg.funcs.encode = &write_emptymsg; + + alltypes.sng_fbytes.funcs.encode = &write_string; + alltypes.sng_fbytes.arg = "3059"; + + alltypes.oneof_msg1.funcs.encode = &write_submsg; + alltypes.oneof_msg1.arg = &oneof_msg1; + } + + alltypes.end.funcs.encode = &write_varint; + alltypes.end.arg = (void*)1099; + + { + uint8_t buffer[2048]; + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + + /* Now encode it and check if we succeeded. */ + if (pb_encode(&stream, AllTypes_fields, &alltypes)) + { + SET_BINARY_MODE(stdout); + fwrite(buffer, 1, stream.bytes_written, stdout); + return 0; /* Success */ + } + else + { + fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream)); + return 1; /* Failure */ + } + } +} diff --git a/tests/anonymous_oneof/SConscript b/tests/anonymous_oneof/SConscript new file mode 100644 index 0000000..20fd1cc --- /dev/null +++ b/tests/anonymous_oneof/SConscript @@ -0,0 +1,30 @@ +# Test anonymous_oneof generator option + +Import('env') + +import re + +match = None +if 'PROTOC_VERSION' in env: + match = re.search('([0-9]+).([0-9]+).([0-9]+)', env['PROTOC_VERSION']) + +if match: + version = list(map(int, match.groups())) + +# Oneof is supported by protoc >= 2.6.0 +if env.GetOption('clean') or (match and (version[0] > 2 or (version[0] == 2 and version[1] >= 6))): + # Anonymous oneofs are supported by clang and gcc + if 'clang' in env['CC'] or 'gcc' in env['CC']: + env2 = env.Clone() + if '-pedantic' in env2['CFLAGS']: + env2['CFLAGS'].remove('-pedantic') + env2.NanopbProto('oneof') + + dec = env2.Program(['decode_oneof.c', + 'oneof.pb.c', + '$COMMON/pb_decode.o', + '$COMMON/pb_common.o']) + + env2.RunTest("message1.txt", [dec, '$BUILD/oneof/message1.pb'], ARGS = ['1']) + env2.RunTest("message2.txt", [dec, '$BUILD/oneof/message2.pb'], ARGS = ['2']) + env2.RunTest("message3.txt", [dec, '$BUILD/oneof/message3.pb'], ARGS = ['3']) diff --git a/tests/anonymous_oneof/decode_oneof.c b/tests/anonymous_oneof/decode_oneof.c new file mode 100644 index 0000000..0f774db --- /dev/null +++ b/tests/anonymous_oneof/decode_oneof.c @@ -0,0 +1,88 @@ +/* Decode a message using oneof fields */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pb_decode.h> +#include "oneof.pb.h" +#include "test_helpers.h" +#include "unittests.h" + +/* Test the 'AnonymousOneOfMessage' */ +int test_oneof_1(pb_istream_t *stream, int option) +{ + AnonymousOneOfMessage msg; + int status = 0; + + /* To better catch initialization errors */ + memset(&msg, 0xAA, sizeof(msg)); + + if (!pb_decode(stream, AnonymousOneOfMessage_fields, &msg)) + { + printf("Decoding failed: %s\n", PB_GET_ERROR(stream)); + return 1; + } + + /* Check that the basic fields work normally */ + TEST(msg.prefix == 123); + TEST(msg.suffix == 321); + + /* Check that we got the right oneof according to command line */ + if (option == 1) + { + TEST(msg.which_values == AnonymousOneOfMessage_first_tag); + TEST(msg.first == 999); + } + else if (option == 2) + { + TEST(msg.which_values == AnonymousOneOfMessage_second_tag); + TEST(strcmp(msg.second, "abcd") == 0); + } + else if (option == 3) + { + TEST(msg.which_values == AnonymousOneOfMessage_third_tag); + TEST(msg.third.array[0] == 1); + TEST(msg.third.array[1] == 2); + TEST(msg.third.array[2] == 3); + TEST(msg.third.array[3] == 4); + TEST(msg.third.array[4] == 5); + } + + return status; +} + +int main(int argc, char **argv) +{ + uint8_t buffer[AnonymousOneOfMessage_size]; + size_t count; + int option; + + if (argc != 2) + { + fprintf(stderr, "Usage: decode_oneof [number]\n"); + return 1; + } + option = atoi(argv[1]); + + SET_BINARY_MODE(stdin); + count = fread(buffer, 1, sizeof(buffer), stdin); + + if (!feof(stdin)) + { + printf("Message does not fit in buffer\n"); + return 1; + } + + { + int status = 0; + pb_istream_t stream; + + stream = pb_istream_from_buffer(buffer, count); + status = test_oneof_1(&stream, option); + + if (status != 0) + return status; + } + + return 0; +} diff --git a/tests/anonymous_oneof/oneof.proto b/tests/anonymous_oneof/oneof.proto new file mode 100644 index 0000000..d56285c --- /dev/null +++ b/tests/anonymous_oneof/oneof.proto @@ -0,0 +1,23 @@ +syntax = "proto2"; + +import 'nanopb.proto'; + +message SubMessage +{ + repeated int32 array = 1 [(nanopb).max_count = 8]; +} + +/* Oneof in a message with other fields */ +message AnonymousOneOfMessage +{ + option (nanopb_msgopt).anonymous_oneof = true; + required int32 prefix = 1; + oneof values + { + int32 first = 5; + string second = 6 [(nanopb).max_size = 8]; + SubMessage third = 7; + } + required int32 suffix = 99; +} + diff --git a/tests/backwards_compatibility/SConscript b/tests/backwards_compatibility/SConscript index 777ef40..81b0318 100644 --- a/tests/backwards_compatibility/SConscript +++ b/tests/backwards_compatibility/SConscript @@ -3,8 +3,8 @@ Import("env") -enc = env.Program(["encode_legacy.c", "alltypes_legacy.c", "$COMMON/pb_encode.o"]) -dec = env.Program(["decode_legacy.c", "alltypes_legacy.c", "$COMMON/pb_decode.o"]) +enc = env.Program(["encode_legacy.c", "alltypes_legacy.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) +dec = env.Program(["decode_legacy.c", "alltypes_legacy.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) env.RunTest(enc) env.RunTest([dec, "encode_legacy.output"]) diff --git a/tests/backwards_compatibility/alltypes_legacy.c b/tests/backwards_compatibility/alltypes_legacy.c index 9134d5e..7311fd4 100644 --- a/tests/backwards_compatibility/alltypes_legacy.c +++ b/tests/backwards_compatibility/alltypes_legacy.c @@ -1,27 +1,37 @@ /* Automatically generated nanopb constant definitions */ -/* Generated by 0.2.0-dev at Sun Feb 17 00:09:53 2013. */ -/* This is a file generated using nanopb-0.2.0-dev. - * It is used as a part of test suite in order to detect any - * incompatible changes made to the generator in future versions. - */ +/* Generated by nanopb-0.3.0-dev at Tue Aug 19 17:53:24 2014. */ #include "alltypes_legacy.h" +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + const char SubMessage_substuff1_default[16] = "1"; const int32_t SubMessage_substuff2_default = 2; -const uint32_t SubMessage_substuff3_default = 3; +const uint32_t SubMessage_substuff3_default = 3u; +const int32_t Limits_int32_min_default = 2147483647; +const int32_t Limits_int32_max_default = -2147483647; +const uint32_t Limits_uint32_min_default = 4294967295u; +const uint32_t Limits_uint32_max_default = 0u; +const int64_t Limits_int64_min_default = 9223372036854775807ll; +const int64_t Limits_int64_max_default = -9223372036854775807ll; +const uint64_t Limits_uint64_min_default = 18446744073709551615ull; +const uint64_t Limits_uint64_max_default = 0ull; +const HugeEnum Limits_enum_min_default = HugeEnum_Positive; +const HugeEnum Limits_enum_max_default = HugeEnum_Negative; const int32_t AllTypes_opt_int32_default = 4041; -const int64_t AllTypes_opt_int64_default = 4042; -const uint32_t AllTypes_opt_uint32_default = 4043; -const uint64_t AllTypes_opt_uint64_default = 4044; +const int64_t AllTypes_opt_int64_default = 4042ll; +const uint32_t AllTypes_opt_uint32_default = 4043u; +const uint64_t AllTypes_opt_uint64_default = 4044ull; const int32_t AllTypes_opt_sint32_default = 4045; const int64_t AllTypes_opt_sint64_default = 4046; const bool AllTypes_opt_bool_default = false; -const uint32_t AllTypes_opt_fixed32_default = 4048; +const uint32_t AllTypes_opt_fixed32_default = 4048u; const int32_t AllTypes_opt_sfixed32_default = 4049; const float AllTypes_opt_float_default = 4050; -const uint64_t AllTypes_opt_fixed64_default = 4051; -const int64_t AllTypes_opt_sfixed64_default = 4052; +const uint64_t AllTypes_opt_fixed64_default = 4051ull; +const int64_t AllTypes_opt_sfixed64_default = 4052ll; const double AllTypes_opt_double_default = 4053; const char AllTypes_opt_string_default[16] = "4054"; const AllTypes_opt_bytes_t AllTypes_opt_bytes_default = {4, {0x34,0x30,0x35,0x35}}; @@ -29,65 +39,115 @@ const MyEnum AllTypes_opt_enum_default = MyEnum_Second; const pb_field_t SubMessage_fields[4] = { - PB_FIELD( 1, STRING , REQUIRED, STATIC, SubMessage, substuff1, substuff1, &SubMessage_substuff1_default), - PB_FIELD( 2, INT32 , REQUIRED, STATIC, SubMessage, substuff2, substuff1, &SubMessage_substuff2_default), - PB_FIELD( 3, FIXED32 , OPTIONAL, STATIC, SubMessage, substuff3, substuff2, &SubMessage_substuff3_default), + PB_FIELD( 1, STRING , REQUIRED, STATIC , FIRST, SubMessage, substuff1, substuff1, &SubMessage_substuff1_default), + PB_FIELD( 2, INT32 , REQUIRED, STATIC , OTHER, SubMessage, substuff2, substuff1, &SubMessage_substuff2_default), + PB_FIELD( 3, FIXED32 , OPTIONAL, STATIC , OTHER, SubMessage, substuff3, substuff2, &SubMessage_substuff3_default), + PB_LAST_FIELD +}; + +const pb_field_t EmptyMessage_fields[1] = { + PB_LAST_FIELD +}; + +const pb_field_t Limits_fields[11] = { + PB_FIELD( 1, INT32 , REQUIRED, STATIC , FIRST, Limits, int32_min, int32_min, &Limits_int32_min_default), + PB_FIELD( 2, INT32 , REQUIRED, STATIC , OTHER, Limits, int32_max, int32_min, &Limits_int32_max_default), + PB_FIELD( 3, UINT32 , REQUIRED, STATIC , OTHER, Limits, uint32_min, int32_max, &Limits_uint32_min_default), + PB_FIELD( 4, UINT32 , REQUIRED, STATIC , OTHER, Limits, uint32_max, uint32_min, &Limits_uint32_max_default), + PB_FIELD( 5, INT64 , REQUIRED, STATIC , OTHER, Limits, int64_min, uint32_max, &Limits_int64_min_default), + PB_FIELD( 6, INT64 , REQUIRED, STATIC , OTHER, Limits, int64_max, int64_min, &Limits_int64_max_default), + PB_FIELD( 7, UINT64 , REQUIRED, STATIC , OTHER, Limits, uint64_min, int64_max, &Limits_uint64_min_default), + PB_FIELD( 8, UINT64 , REQUIRED, STATIC , OTHER, Limits, uint64_max, uint64_min, &Limits_uint64_max_default), + PB_FIELD( 9, ENUM , REQUIRED, STATIC , OTHER, Limits, enum_min, uint64_max, &Limits_enum_min_default), + PB_FIELD( 10, ENUM , REQUIRED, STATIC , OTHER, Limits, enum_max, enum_min, &Limits_enum_max_default), PB_LAST_FIELD }; -const pb_field_t AllTypes_fields[53] = { - PB_FIELD( 1, INT32 , REQUIRED, STATIC, AllTypes, req_int32, req_int32, 0), - PB_FIELD( 2, INT64 , REQUIRED, STATIC, AllTypes, req_int64, req_int32, 0), - PB_FIELD( 3, UINT32 , REQUIRED, STATIC, AllTypes, req_uint32, req_int64, 0), - PB_FIELD( 4, UINT64 , REQUIRED, STATIC, AllTypes, req_uint64, req_uint32, 0), - PB_FIELD( 5, SINT32 , REQUIRED, STATIC, AllTypes, req_sint32, req_uint64, 0), - PB_FIELD( 6, SINT64 , REQUIRED, STATIC, AllTypes, req_sint64, req_sint32, 0), - PB_FIELD( 7, BOOL , REQUIRED, STATIC, AllTypes, req_bool, req_sint64, 0), - PB_FIELD( 8, FIXED32 , REQUIRED, STATIC, AllTypes, req_fixed32, req_bool, 0), - PB_FIELD( 9, SFIXED32, REQUIRED, STATIC, AllTypes, req_sfixed32, req_fixed32, 0), - PB_FIELD( 10, FLOAT , REQUIRED, STATIC, AllTypes, req_float, req_sfixed32, 0), - PB_FIELD( 11, FIXED64 , REQUIRED, STATIC, AllTypes, req_fixed64, req_float, 0), - PB_FIELD( 12, SFIXED64, REQUIRED, STATIC, AllTypes, req_sfixed64, req_fixed64, 0), - PB_FIELD( 13, DOUBLE , REQUIRED, STATIC, AllTypes, req_double, req_sfixed64, 0), - PB_FIELD( 14, STRING , REQUIRED, STATIC, AllTypes, req_string, req_double, 0), - PB_FIELD( 15, BYTES , REQUIRED, STATIC, AllTypes, req_bytes, req_string, 0), - PB_FIELD( 16, MESSAGE , REQUIRED, STATIC, AllTypes, req_submsg, req_bytes, &SubMessage_fields), - PB_FIELD( 17, ENUM , REQUIRED, STATIC, AllTypes, req_enum, req_submsg, 0), - PB_FIELD( 21, INT32 , REPEATED, STATIC, AllTypes, rep_int32, req_enum, 0), - PB_FIELD( 22, INT64 , REPEATED, STATIC, AllTypes, rep_int64, rep_int32, 0), - PB_FIELD( 23, UINT32 , REPEATED, STATIC, AllTypes, rep_uint32, rep_int64, 0), - PB_FIELD( 24, UINT64 , REPEATED, STATIC, AllTypes, rep_uint64, rep_uint32, 0), - PB_FIELD( 25, SINT32 , REPEATED, STATIC, AllTypes, rep_sint32, rep_uint64, 0), - PB_FIELD( 26, SINT64 , REPEATED, STATIC, AllTypes, rep_sint64, rep_sint32, 0), - PB_FIELD( 27, BOOL , REPEATED, STATIC, AllTypes, rep_bool, rep_sint64, 0), - PB_FIELD( 28, FIXED32 , REPEATED, STATIC, AllTypes, rep_fixed32, rep_bool, 0), - PB_FIELD( 29, SFIXED32, REPEATED, STATIC, AllTypes, rep_sfixed32, rep_fixed32, 0), - PB_FIELD( 30, FLOAT , REPEATED, STATIC, AllTypes, rep_float, rep_sfixed32, 0), - PB_FIELD( 31, FIXED64 , REPEATED, STATIC, AllTypes, rep_fixed64, rep_float, 0), - PB_FIELD( 32, SFIXED64, REPEATED, STATIC, AllTypes, rep_sfixed64, rep_fixed64, 0), - PB_FIELD( 33, DOUBLE , REPEATED, STATIC, AllTypes, rep_double, rep_sfixed64, 0), - PB_FIELD( 34, STRING , REPEATED, STATIC, AllTypes, rep_string, rep_double, 0), - PB_FIELD( 35, BYTES , REPEATED, STATIC, AllTypes, rep_bytes, rep_string, 0), - PB_FIELD( 36, MESSAGE , REPEATED, STATIC, AllTypes, rep_submsg, rep_bytes, &SubMessage_fields), - PB_FIELD( 37, ENUM , REPEATED, STATIC, AllTypes, rep_enum, rep_submsg, 0), - PB_FIELD( 41, INT32 , OPTIONAL, STATIC, AllTypes, opt_int32, rep_enum, &AllTypes_opt_int32_default), - PB_FIELD( 42, INT64 , OPTIONAL, STATIC, AllTypes, opt_int64, opt_int32, &AllTypes_opt_int64_default), - PB_FIELD( 43, UINT32 , OPTIONAL, STATIC, AllTypes, opt_uint32, opt_int64, &AllTypes_opt_uint32_default), - PB_FIELD( 44, UINT64 , OPTIONAL, STATIC, AllTypes, opt_uint64, opt_uint32, &AllTypes_opt_uint64_default), - PB_FIELD( 45, SINT32 , OPTIONAL, STATIC, AllTypes, opt_sint32, opt_uint64, &AllTypes_opt_sint32_default), - PB_FIELD( 46, SINT64 , OPTIONAL, STATIC, AllTypes, opt_sint64, opt_sint32, &AllTypes_opt_sint64_default), - PB_FIELD( 47, BOOL , OPTIONAL, STATIC, AllTypes, opt_bool, opt_sint64, &AllTypes_opt_bool_default), - PB_FIELD( 48, FIXED32 , OPTIONAL, STATIC, AllTypes, opt_fixed32, opt_bool, &AllTypes_opt_fixed32_default), - PB_FIELD( 49, SFIXED32, OPTIONAL, STATIC, AllTypes, opt_sfixed32, opt_fixed32, &AllTypes_opt_sfixed32_default), - PB_FIELD( 50, FLOAT , OPTIONAL, STATIC, AllTypes, opt_float, opt_sfixed32, &AllTypes_opt_float_default), - PB_FIELD( 51, FIXED64 , OPTIONAL, STATIC, AllTypes, opt_fixed64, opt_float, &AllTypes_opt_fixed64_default), - PB_FIELD( 52, SFIXED64, OPTIONAL, STATIC, AllTypes, opt_sfixed64, opt_fixed64, &AllTypes_opt_sfixed64_default), - PB_FIELD( 53, DOUBLE , OPTIONAL, STATIC, AllTypes, opt_double, opt_sfixed64, &AllTypes_opt_double_default), - PB_FIELD( 54, STRING , OPTIONAL, STATIC, AllTypes, opt_string, opt_double, &AllTypes_opt_string_default), - PB_FIELD( 55, BYTES , OPTIONAL, STATIC, AllTypes, opt_bytes, opt_string, &AllTypes_opt_bytes_default), - PB_FIELD( 56, MESSAGE , OPTIONAL, STATIC, AllTypes, opt_submsg, opt_bytes, &SubMessage_fields), - PB_FIELD( 57, ENUM , OPTIONAL, STATIC, AllTypes, opt_enum, opt_submsg, &AllTypes_opt_enum_default), - PB_FIELD( 99, INT32 , REQUIRED, STATIC, AllTypes, end, opt_enum, 0), +const pb_field_t AllTypes_fields[54] = { + PB_FIELD( 1, INT32 , REQUIRED, STATIC , FIRST, AllTypes, req_int32, req_int32, 0), + PB_FIELD( 2, INT64 , REQUIRED, STATIC , OTHER, AllTypes, req_int64, req_int32, 0), + PB_FIELD( 3, UINT32 , REQUIRED, STATIC , OTHER, AllTypes, req_uint32, req_int64, 0), + PB_FIELD( 4, UINT64 , REQUIRED, STATIC , OTHER, AllTypes, req_uint64, req_uint32, 0), + PB_FIELD( 5, SINT32 , REQUIRED, STATIC , OTHER, AllTypes, req_sint32, req_uint64, 0), + PB_FIELD( 6, SINT64 , REQUIRED, STATIC , OTHER, AllTypes, req_sint64, req_sint32, 0), + PB_FIELD( 7, BOOL , REQUIRED, STATIC , OTHER, AllTypes, req_bool, req_sint64, 0), + PB_FIELD( 8, FIXED32 , REQUIRED, STATIC , OTHER, AllTypes, req_fixed32, req_bool, 0), + PB_FIELD( 9, SFIXED32, REQUIRED, STATIC , OTHER, AllTypes, req_sfixed32, req_fixed32, 0), + PB_FIELD( 10, FLOAT , REQUIRED, STATIC , OTHER, AllTypes, req_float, req_sfixed32, 0), + PB_FIELD( 11, FIXED64 , REQUIRED, STATIC , OTHER, AllTypes, req_fixed64, req_float, 0), + PB_FIELD( 12, SFIXED64, REQUIRED, STATIC , OTHER, AllTypes, req_sfixed64, req_fixed64, 0), + PB_FIELD( 13, DOUBLE , REQUIRED, STATIC , OTHER, AllTypes, req_double, req_sfixed64, 0), + PB_FIELD( 14, STRING , REQUIRED, STATIC , OTHER, AllTypes, req_string, req_double, 0), + PB_FIELD( 15, BYTES , REQUIRED, STATIC , OTHER, AllTypes, req_bytes, req_string, 0), + PB_FIELD( 16, MESSAGE , REQUIRED, STATIC , OTHER, AllTypes, req_submsg, req_bytes, &SubMessage_fields), + PB_FIELD( 17, ENUM , REQUIRED, STATIC , OTHER, AllTypes, req_enum, req_submsg, 0), + PB_FIELD( 21, INT32 , REPEATED, STATIC , OTHER, AllTypes, rep_int32, req_enum, 0), + PB_FIELD( 22, INT64 , REPEATED, STATIC , OTHER, AllTypes, rep_int64, rep_int32, 0), + PB_FIELD( 23, UINT32 , REPEATED, STATIC , OTHER, AllTypes, rep_uint32, rep_int64, 0), + PB_FIELD( 24, UINT64 , REPEATED, STATIC , OTHER, AllTypes, rep_uint64, rep_uint32, 0), + PB_FIELD( 25, SINT32 , REPEATED, STATIC , OTHER, AllTypes, rep_sint32, rep_uint64, 0), + PB_FIELD( 26, SINT64 , REPEATED, STATIC , OTHER, AllTypes, rep_sint64, rep_sint32, 0), + PB_FIELD( 27, BOOL , REPEATED, STATIC , OTHER, AllTypes, rep_bool, rep_sint64, 0), + PB_FIELD( 28, FIXED32 , REPEATED, STATIC , OTHER, AllTypes, rep_fixed32, rep_bool, 0), + PB_FIELD( 29, SFIXED32, REPEATED, STATIC , OTHER, AllTypes, rep_sfixed32, rep_fixed32, 0), + PB_FIELD( 30, FLOAT , REPEATED, STATIC , OTHER, AllTypes, rep_float, rep_sfixed32, 0), + PB_FIELD( 31, FIXED64 , REPEATED, STATIC , OTHER, AllTypes, rep_fixed64, rep_float, 0), + PB_FIELD( 32, SFIXED64, REPEATED, STATIC , OTHER, AllTypes, rep_sfixed64, rep_fixed64, 0), + PB_FIELD( 33, DOUBLE , REPEATED, STATIC , OTHER, AllTypes, rep_double, rep_sfixed64, 0), + PB_FIELD( 34, STRING , REPEATED, STATIC , OTHER, AllTypes, rep_string, rep_double, 0), + PB_FIELD( 35, BYTES , REPEATED, STATIC , OTHER, AllTypes, rep_bytes, rep_string, 0), + PB_FIELD( 36, MESSAGE , REPEATED, STATIC , OTHER, AllTypes, rep_submsg, rep_bytes, &SubMessage_fields), + PB_FIELD( 37, ENUM , REPEATED, STATIC , OTHER, AllTypes, rep_enum, rep_submsg, 0), + PB_FIELD( 41, INT32 , OPTIONAL, STATIC , OTHER, AllTypes, opt_int32, rep_enum, &AllTypes_opt_int32_default), + PB_FIELD( 42, INT64 , OPTIONAL, STATIC , OTHER, AllTypes, opt_int64, opt_int32, &AllTypes_opt_int64_default), + PB_FIELD( 43, UINT32 , OPTIONAL, STATIC , OTHER, AllTypes, opt_uint32, opt_int64, &AllTypes_opt_uint32_default), + PB_FIELD( 44, UINT64 , OPTIONAL, STATIC , OTHER, AllTypes, opt_uint64, opt_uint32, &AllTypes_opt_uint64_default), + PB_FIELD( 45, SINT32 , OPTIONAL, STATIC , OTHER, AllTypes, opt_sint32, opt_uint64, &AllTypes_opt_sint32_default), + PB_FIELD( 46, SINT64 , OPTIONAL, STATIC , OTHER, AllTypes, opt_sint64, opt_sint32, &AllTypes_opt_sint64_default), + PB_FIELD( 47, BOOL , OPTIONAL, STATIC , OTHER, AllTypes, opt_bool, opt_sint64, &AllTypes_opt_bool_default), + PB_FIELD( 48, FIXED32 , OPTIONAL, STATIC , OTHER, AllTypes, opt_fixed32, opt_bool, &AllTypes_opt_fixed32_default), + PB_FIELD( 49, SFIXED32, OPTIONAL, STATIC , OTHER, AllTypes, opt_sfixed32, opt_fixed32, &AllTypes_opt_sfixed32_default), + PB_FIELD( 50, FLOAT , OPTIONAL, STATIC , OTHER, AllTypes, opt_float, opt_sfixed32, &AllTypes_opt_float_default), + PB_FIELD( 51, FIXED64 , OPTIONAL, STATIC , OTHER, AllTypes, opt_fixed64, opt_float, &AllTypes_opt_fixed64_default), + PB_FIELD( 52, SFIXED64, OPTIONAL, STATIC , OTHER, AllTypes, opt_sfixed64, opt_fixed64, &AllTypes_opt_sfixed64_default), + PB_FIELD( 53, DOUBLE , OPTIONAL, STATIC , OTHER, AllTypes, opt_double, opt_sfixed64, &AllTypes_opt_double_default), + PB_FIELD( 54, STRING , OPTIONAL, STATIC , OTHER, AllTypes, opt_string, opt_double, &AllTypes_opt_string_default), + PB_FIELD( 55, BYTES , OPTIONAL, STATIC , OTHER, AllTypes, opt_bytes, opt_string, &AllTypes_opt_bytes_default), + PB_FIELD( 56, MESSAGE , OPTIONAL, STATIC , OTHER, AllTypes, opt_submsg, opt_bytes, &SubMessage_fields), + PB_FIELD( 57, ENUM , OPTIONAL, STATIC , OTHER, AllTypes, opt_enum, opt_submsg, &AllTypes_opt_enum_default), + PB_FIELD( 99, INT32 , REQUIRED, STATIC , OTHER, AllTypes, end, opt_enum, 0), + PB_FIELD(200, EXTENSION, OPTIONAL, CALLBACK, OTHER, AllTypes, extensions, end, 0), PB_LAST_FIELD }; + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(AllTypes, req_submsg) < 65536 && pb_membersize(AllTypes, rep_submsg[0]) < 65536 && pb_membersize(AllTypes, opt_submsg) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_SubMessage_EmptyMessage_Limits_AllTypes) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(AllTypes, req_submsg) < 256 && pb_membersize(AllTypes, rep_submsg[0]) < 256 && pb_membersize(AllTypes, opt_submsg) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_SubMessage_EmptyMessage_Limits_AllTypes) +#endif + + +/* On some platforms (such as AVR), double is really float. + * These are not directly supported by nanopb, but see example_avr_double. + * To get rid of this error, remove any double fields from your .proto. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) + diff --git a/tests/backwards_compatibility/alltypes_legacy.h b/tests/backwards_compatibility/alltypes_legacy.h index 037b347..4e0a63b 100644 --- a/tests/backwards_compatibility/alltypes_legacy.h +++ b/tests/backwards_compatibility/alltypes_legacy.h @@ -1,18 +1,24 @@ /* Automatically generated nanopb header */ -/* This is a file generated using nanopb-0.2.0-dev. - * It is used as a part of test suite in order to detect any - * incompatible changes made to the generator in future versions. - */ +/* Generated by nanopb-0.3.0-dev at Tue Aug 19 17:53:24 2014. */ -#ifndef _PB_ALLTYPES_PB_H_ -#define _PB_ALLTYPES_PB_H_ +#ifndef PB_ALLTYPES_LEGACY_H_INCLUDED +#define PB_ALLTYPES_LEGACY_H_INCLUDED #include <pb.h> +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + #ifdef __cplusplus extern "C" { #endif /* Enum definitions */ +typedef enum _HugeEnum { + HugeEnum_Negative = -2147483647, + HugeEnum_Positive = 2147483647 +} HugeEnum; + typedef enum _MyEnum { MyEnum_Zero = 0, MyEnum_First = 1, @@ -21,6 +27,23 @@ typedef enum _MyEnum { } MyEnum; /* Struct definitions */ +typedef struct _EmptyMessage { + uint8_t dummy_field; +} EmptyMessage; + +typedef struct _Limits { + int32_t int32_min; + int32_t int32_max; + uint32_t uint32_min; + uint32_t uint32_max; + int64_t int64_min; + int64_t int64_max; + uint64_t uint64_min; + uint64_t uint64_max; + HugeEnum enum_min; + HugeEnum enum_max; +} Limits; + typedef struct _SubMessage { char substuff1[16]; int32_t substuff2; @@ -28,20 +51,11 @@ typedef struct _SubMessage { uint32_t substuff3; } SubMessage; -typedef struct { - size_t size; - uint8_t bytes[16]; -} AllTypes_req_bytes_t; +typedef PB_BYTES_ARRAY_T(16) AllTypes_req_bytes_t; -typedef struct { - size_t size; - uint8_t bytes[16]; -} AllTypes_rep_bytes_t; +typedef PB_BYTES_ARRAY_T(16) AllTypes_rep_bytes_t; -typedef struct { - size_t size; - uint8_t bytes[16]; -} AllTypes_opt_bytes_t; +typedef PB_BYTES_ARRAY_T(16) AllTypes_opt_bytes_t; typedef struct _AllTypes { int32_t req_int32; @@ -61,39 +75,39 @@ typedef struct _AllTypes { AllTypes_req_bytes_t req_bytes; SubMessage req_submsg; MyEnum req_enum; - size_t rep_int32_count; + pb_size_t rep_int32_count; int32_t rep_int32[5]; - size_t rep_int64_count; + pb_size_t rep_int64_count; int64_t rep_int64[5]; - size_t rep_uint32_count; + pb_size_t rep_uint32_count; uint32_t rep_uint32[5]; - size_t rep_uint64_count; + pb_size_t rep_uint64_count; uint64_t rep_uint64[5]; - size_t rep_sint32_count; + pb_size_t rep_sint32_count; int32_t rep_sint32[5]; - size_t rep_sint64_count; + pb_size_t rep_sint64_count; int64_t rep_sint64[5]; - size_t rep_bool_count; + pb_size_t rep_bool_count; bool rep_bool[5]; - size_t rep_fixed32_count; + pb_size_t rep_fixed32_count; uint32_t rep_fixed32[5]; - size_t rep_sfixed32_count; + pb_size_t rep_sfixed32_count; int32_t rep_sfixed32[5]; - size_t rep_float_count; + pb_size_t rep_float_count; float rep_float[5]; - size_t rep_fixed64_count; + pb_size_t rep_fixed64_count; uint64_t rep_fixed64[5]; - size_t rep_sfixed64_count; + pb_size_t rep_sfixed64_count; int64_t rep_sfixed64[5]; - size_t rep_double_count; + pb_size_t rep_double_count; double rep_double[5]; - size_t rep_string_count; + pb_size_t rep_string_count; char rep_string[5][16]; - size_t rep_bytes_count; + pb_size_t rep_bytes_count; AllTypes_rep_bytes_t rep_bytes[5]; - size_t rep_submsg_count; + pb_size_t rep_submsg_count; SubMessage rep_submsg[5]; - size_t rep_enum_count; + pb_size_t rep_enum_count; MyEnum rep_enum[5]; bool has_opt_int32; int32_t opt_int32; @@ -130,12 +144,23 @@ typedef struct _AllTypes { bool has_opt_enum; MyEnum opt_enum; int32_t end; + pb_extension_t *extensions; } AllTypes; /* Default values for struct fields */ extern const char SubMessage_substuff1_default[16]; extern const int32_t SubMessage_substuff2_default; extern const uint32_t SubMessage_substuff3_default; +extern const int32_t Limits_int32_min_default; +extern const int32_t Limits_int32_max_default; +extern const uint32_t Limits_uint32_min_default; +extern const uint32_t Limits_uint32_max_default; +extern const int64_t Limits_int64_min_default; +extern const int64_t Limits_int64_max_default; +extern const uint64_t Limits_uint64_min_default; +extern const uint64_t Limits_uint64_max_default; +extern const HugeEnum Limits_enum_min_default; +extern const HugeEnum Limits_enum_max_default; extern const int32_t AllTypes_opt_int32_default; extern const int64_t AllTypes_opt_int64_default; extern const uint32_t AllTypes_opt_uint32_default; @@ -153,23 +178,94 @@ extern const char AllTypes_opt_string_default[16]; extern const AllTypes_opt_bytes_t AllTypes_opt_bytes_default; extern const MyEnum AllTypes_opt_enum_default; -/* Struct field encoding specification for nanopb */ -extern const pb_field_t SubMessage_fields[4]; -extern const pb_field_t AllTypes_fields[53]; +/* Initializer values for message structs */ +#define SubMessage_init_default {"1", 2, false, 3u} +#define EmptyMessage_init_default {0} +#define Limits_init_default {2147483647, -2147483647, 4294967295u, 0u, 9223372036854775807ll, -9223372036854775807ll, 18446744073709551615ull, 0ull, HugeEnum_Positive, HugeEnum_Negative} +#define AllTypes_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", {0, {0}}, SubMessage_init_default, (MyEnum)0, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {"", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0, {SubMessage_init_default, SubMessage_init_default, SubMessage_init_default, SubMessage_init_default, SubMessage_init_default}, 0, {(MyEnum)0, (MyEnum)0, (MyEnum)0, (MyEnum)0, (MyEnum)0}, false, 4041, false, 4042ll, false, 4043u, false, 4044ull, false, 4045, false, 4046, false, false, false, 4048u, false, 4049, false, 4050, false, 4051ull, false, 4052ll, false, 4053, false, "4054", false, {4, {0x34,0x30,0x35,0x35}}, false, SubMessage_init_default, false, MyEnum_Second, 0, NULL} +#define SubMessage_init_zero {"", 0, false, 0} +#define EmptyMessage_init_zero {0} +#define Limits_init_zero {0, 0, 0, 0, 0, 0, 0, 0, (HugeEnum)0, (HugeEnum)0} +#define AllTypes_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", {0, {0}}, SubMessage_init_zero, (MyEnum)0, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0}, 0, {"", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0, {SubMessage_init_zero, SubMessage_init_zero, SubMessage_init_zero, SubMessage_init_zero, SubMessage_init_zero}, 0, {(MyEnum)0, (MyEnum)0, (MyEnum)0, (MyEnum)0, (MyEnum)0}, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, "", false, {0, {0}}, false, SubMessage_init_zero, false, (MyEnum)0, 0, NULL} -/* Check that field information fits in pb_field_t */ -#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) -STATIC_ASSERT((pb_membersize(AllTypes, req_submsg) < 256 && pb_membersize(AllTypes, rep_submsg[0]) < 256 && pb_membersize(AllTypes, opt_submsg) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_SubMessage_AllTypes) -#endif +/* Field tags (for use in manual encoding/decoding) */ +#define Limits_int32_min_tag 1 +#define Limits_int32_max_tag 2 +#define Limits_uint32_min_tag 3 +#define Limits_uint32_max_tag 4 +#define Limits_int64_min_tag 5 +#define Limits_int64_max_tag 6 +#define Limits_uint64_min_tag 7 +#define Limits_uint64_max_tag 8 +#define Limits_enum_min_tag 9 +#define Limits_enum_max_tag 10 +#define SubMessage_substuff1_tag 1 +#define SubMessage_substuff2_tag 2 +#define SubMessage_substuff3_tag 3 +#define AllTypes_req_int32_tag 1 +#define AllTypes_req_int64_tag 2 +#define AllTypes_req_uint32_tag 3 +#define AllTypes_req_uint64_tag 4 +#define AllTypes_req_sint32_tag 5 +#define AllTypes_req_sint64_tag 6 +#define AllTypes_req_bool_tag 7 +#define AllTypes_req_fixed32_tag 8 +#define AllTypes_req_sfixed32_tag 9 +#define AllTypes_req_float_tag 10 +#define AllTypes_req_fixed64_tag 11 +#define AllTypes_req_sfixed64_tag 12 +#define AllTypes_req_double_tag 13 +#define AllTypes_req_string_tag 14 +#define AllTypes_req_bytes_tag 15 +#define AllTypes_req_submsg_tag 16 +#define AllTypes_req_enum_tag 17 +#define AllTypes_rep_int32_tag 21 +#define AllTypes_rep_int64_tag 22 +#define AllTypes_rep_uint32_tag 23 +#define AllTypes_rep_uint64_tag 24 +#define AllTypes_rep_sint32_tag 25 +#define AllTypes_rep_sint64_tag 26 +#define AllTypes_rep_bool_tag 27 +#define AllTypes_rep_fixed32_tag 28 +#define AllTypes_rep_sfixed32_tag 29 +#define AllTypes_rep_float_tag 30 +#define AllTypes_rep_fixed64_tag 31 +#define AllTypes_rep_sfixed64_tag 32 +#define AllTypes_rep_double_tag 33 +#define AllTypes_rep_string_tag 34 +#define AllTypes_rep_bytes_tag 35 +#define AllTypes_rep_submsg_tag 36 +#define AllTypes_rep_enum_tag 37 +#define AllTypes_opt_int32_tag 41 +#define AllTypes_opt_int64_tag 42 +#define AllTypes_opt_uint32_tag 43 +#define AllTypes_opt_uint64_tag 44 +#define AllTypes_opt_sint32_tag 45 +#define AllTypes_opt_sint64_tag 46 +#define AllTypes_opt_bool_tag 47 +#define AllTypes_opt_fixed32_tag 48 +#define AllTypes_opt_sfixed32_tag 49 +#define AllTypes_opt_float_tag 50 +#define AllTypes_opt_fixed64_tag 51 +#define AllTypes_opt_sfixed64_tag 52 +#define AllTypes_opt_double_tag 53 +#define AllTypes_opt_string_tag 54 +#define AllTypes_opt_bytes_tag 55 +#define AllTypes_opt_submsg_tag 56 +#define AllTypes_opt_enum_tag 57 +#define AllTypes_end_tag 99 -#if !defined(PB_FIELD_32BIT) -STATIC_ASSERT((pb_membersize(AllTypes, req_submsg) < 65536 && pb_membersize(AllTypes, rep_submsg[0]) < 65536 && pb_membersize(AllTypes, opt_submsg) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_SubMessage_AllTypes) -#endif +/* Struct field encoding specification for nanopb */ +extern const pb_field_t SubMessage_fields[4]; +extern const pb_field_t EmptyMessage_fields[1]; +extern const pb_field_t Limits_fields[11]; +extern const pb_field_t AllTypes_fields[54]; -/* On some platforms (such as AVR), double is really float. - * These are not directly supported by nanopb, but see example_avr_double. - */ -STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) +/* Maximum encoded size of messages (where known) */ +#define SubMessage_size 34 +#define EmptyMessage_size 0 +#define Limits_size 90 +#define AllTypes_size 1362 #ifdef __cplusplus } /* extern "C" */ diff --git a/tests/backwards_compatibility/alltypes_legacy.options b/tests/backwards_compatibility/alltypes_legacy.options new file mode 100644 index 0000000..b31e3cf --- /dev/null +++ b/tests/backwards_compatibility/alltypes_legacy.options @@ -0,0 +1,3 @@ +* max_size:16 +* max_count:5 + diff --git a/tests/backwards_compatibility/alltypes_legacy.proto b/tests/backwards_compatibility/alltypes_legacy.proto new file mode 100644 index 0000000..f5bc35c --- /dev/null +++ b/tests/backwards_compatibility/alltypes_legacy.proto @@ -0,0 +1,110 @@ +syntax = "proto2"; + +message SubMessage { + required string substuff1 = 1 [default = "1"]; + required int32 substuff2 = 2 [default = 2]; + optional fixed32 substuff3 = 3 [default = 3]; +} + +message EmptyMessage { + +} + +enum HugeEnum { + Negative = -2147483647; /* protoc doesn't accept -2147483648 here */ + Positive = 2147483647; +} + +message Limits { + required int32 int32_min = 1 [default = 2147483647]; + required int32 int32_max = 2 [default = -2147483647]; + required uint32 uint32_min = 3 [default = 4294967295]; + required uint32 uint32_max = 4 [default = 0]; + required int64 int64_min = 5 [default = 9223372036854775807]; + required int64 int64_max = 6 [default = -9223372036854775807]; + required uint64 uint64_min = 7 [default = 18446744073709551615]; + required uint64 uint64_max = 8 [default = 0]; + required HugeEnum enum_min = 9 [default = Positive]; + required HugeEnum enum_max = 10 [default = Negative]; +} + +enum MyEnum { + Zero = 0; + First = 1; + Second = 2; + Truth = 42; +} + +message AllTypes { + required int32 req_int32 = 1; + required int64 req_int64 = 2; + required uint32 req_uint32 = 3; + required uint64 req_uint64 = 4; + required sint32 req_sint32 = 5; + required sint64 req_sint64 = 6; + required bool req_bool = 7; + + required fixed32 req_fixed32 = 8; + required sfixed32 req_sfixed32= 9; + required float req_float = 10; + + required fixed64 req_fixed64 = 11; + required sfixed64 req_sfixed64= 12; + required double req_double = 13; + + required string req_string = 14; + required bytes req_bytes = 15; + required SubMessage req_submsg = 16; + required MyEnum req_enum = 17; + + + repeated int32 rep_int32 = 21 [packed = true]; + repeated int64 rep_int64 = 22 [packed = true]; + repeated uint32 rep_uint32 = 23 [packed = true]; + repeated uint64 rep_uint64 = 24 [packed = true]; + repeated sint32 rep_sint32 = 25 [packed = true]; + repeated sint64 rep_sint64 = 26 [packed = true]; + repeated bool rep_bool = 27 [packed = true]; + + repeated fixed32 rep_fixed32 = 28 [packed = true]; + repeated sfixed32 rep_sfixed32= 29 [packed = true]; + repeated float rep_float = 30 [packed = true]; + + repeated fixed64 rep_fixed64 = 31 [packed = true]; + repeated sfixed64 rep_sfixed64= 32 [packed = true]; + repeated double rep_double = 33 [packed = true]; + + repeated string rep_string = 34; + repeated bytes rep_bytes = 35; + repeated SubMessage rep_submsg = 36; + repeated MyEnum rep_enum = 37 [packed = true]; + + optional int32 opt_int32 = 41 [default = 4041]; + optional int64 opt_int64 = 42 [default = 4042]; + optional uint32 opt_uint32 = 43 [default = 4043]; + optional uint64 opt_uint64 = 44 [default = 4044]; + optional sint32 opt_sint32 = 45 [default = 4045]; + optional sint64 opt_sint64 = 46 [default = 4046]; + optional bool opt_bool = 47 [default = false]; + + optional fixed32 opt_fixed32 = 48 [default = 4048]; + optional sfixed32 opt_sfixed32= 49 [default = 4049]; + optional float opt_float = 50 [default = 4050]; + + optional fixed64 opt_fixed64 = 51 [default = 4051]; + optional sfixed64 opt_sfixed64= 52 [default = 4052]; + optional double opt_double = 53 [default = 4053]; + + optional string opt_string = 54 [default = "4054"]; + optional bytes opt_bytes = 55 [default = "4055"]; + optional SubMessage opt_submsg = 56; + optional MyEnum opt_enum = 57 [default = Second]; + + // Just to make sure that the size of the fields has been calculated + // properly, i.e. otherwise a bug in last field might not be detected. + required int32 end = 99; + + + extensions 200 to 255; +} + diff --git a/tests/backwards_compatibility/decode_legacy.c b/tests/backwards_compatibility/decode_legacy.c index 9dfe437..5f5b6bb 100644 --- a/tests/backwards_compatibility/decode_legacy.c +++ b/tests/backwards_compatibility/decode_legacy.c @@ -22,10 +22,7 @@ the decoding and checks the fields. */ bool check_alltypes(pb_istream_t *stream, int mode) { - AllTypes alltypes; - - /* Fill with garbage to better detect initialization errors */ - memset(&alltypes, 0xAA, sizeof(alltypes)); + AllTypes alltypes = {0}; if (!pb_decode(stream, AllTypes_fields, &alltypes)) return false; diff --git a/tests/basic_buffer/SConscript b/tests/basic_buffer/SConscript index 2546aaa..acaf5ff 100644 --- a/tests/basic_buffer/SConscript +++ b/tests/basic_buffer/SConscript @@ -2,8 +2,8 @@ Import("env") -enc = env.Program(["encode_buffer.c", "$COMMON/person.pb.c", "$COMMON/pb_encode.o"]) -dec = env.Program(["decode_buffer.c", "$COMMON/person.pb.c", "$COMMON/pb_decode.o"]) +enc = env.Program(["encode_buffer.c", "$COMMON/person.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) +dec = env.Program(["decode_buffer.c", "$COMMON/person.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) env.RunTest(enc) env.RunTest([dec, "encode_buffer.output"]) diff --git a/tests/basic_buffer/decode_buffer.c b/tests/basic_buffer/decode_buffer.c index fae9e2f..291d164 100644 --- a/tests/basic_buffer/decode_buffer.c +++ b/tests/basic_buffer/decode_buffer.c @@ -16,7 +16,7 @@ bool print_person(pb_istream_t *stream) { int i; - Person person; + Person person = Person_init_zero; if (!pb_decode(stream, Person_fields, &person)) return false; diff --git a/tests/basic_stream/SConscript b/tests/basic_stream/SConscript index 46db8c4..7d66856 100644 --- a/tests/basic_stream/SConscript +++ b/tests/basic_stream/SConscript @@ -2,8 +2,8 @@ Import("env") -enc = env.Program(["encode_stream.c", "$COMMON/person.pb.c", "$COMMON/pb_encode.o"]) -dec = env.Program(["decode_stream.c", "$COMMON/person.pb.c", "$COMMON/pb_decode.o"]) +enc = env.Program(["encode_stream.c", "$COMMON/person.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) +dec = env.Program(["decode_stream.c", "$COMMON/person.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) env.RunTest(enc) env.RunTest([dec, "encode_stream.output"]) diff --git a/tests/basic_stream/decode_stream.c b/tests/basic_stream/decode_stream.c index 667bf3c..798dcc5 100644 --- a/tests/basic_stream/decode_stream.c +++ b/tests/basic_stream/decode_stream.c @@ -12,7 +12,7 @@ bool print_person(pb_istream_t *stream) { int i; - Person person; + Person person = Person_init_zero; if (!pb_decode(stream, Person_fields, &person)) return false; diff --git a/tests/buffer_only/SConscript b/tests/buffer_only/SConscript index cddbb04..55b747b 100644 --- a/tests/buffer_only/SConscript +++ b/tests/buffer_only/SConscript @@ -18,10 +18,11 @@ strict = opts.Clone() strict.Append(CFLAGS = strict['CORECFLAGS']) strict.Object("pb_decode_bufonly.o", "$NANOPB/pb_decode.c") strict.Object("pb_encode_bufonly.o", "$NANOPB/pb_encode.c") +strict.Object("pb_common_bufonly.o", "$NANOPB/pb_common.c") # Now build and run the test normally. -enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_bufonly.o"]) -dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_bufonly.o"]) +enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_bufonly.o", "pb_common_bufonly.o"]) +dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_bufonly.o", "pb_common_bufonly.o"]) env.RunTest(enc) env.RunTest([dec, "encode_alltypes.output"]) diff --git a/tests/callbacks/SConscript b/tests/callbacks/SConscript index 9ec8a43..4452143 100644 --- a/tests/callbacks/SConscript +++ b/tests/callbacks/SConscript @@ -3,8 +3,8 @@ Import("env") env.NanopbProto("callbacks") -enc = env.Program(["encode_callbacks.c", "callbacks.pb.c", "$COMMON/pb_encode.o"]) -dec = env.Program(["decode_callbacks.c", "callbacks.pb.c", "$COMMON/pb_decode.o"]) +enc = env.Program(["encode_callbacks.c", "callbacks.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) +dec = env.Program(["decode_callbacks.c", "callbacks.pb.c", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) env.RunTest(enc) env.RunTest([dec, "encode_callbacks.output"]) diff --git a/tests/callbacks/callbacks.proto b/tests/callbacks/callbacks.proto index ccd1edd..96ac744 100644 --- a/tests/callbacks/callbacks.proto +++ b/tests/callbacks/callbacks.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + message SubMessage { optional string stringvalue = 1; repeated int32 int32value = 2; diff --git a/tests/common/SConscript b/tests/common/SConscript index 144f149..05e2f85 100644 --- a/tests/common/SConscript +++ b/tests/common/SConscript @@ -8,10 +8,41 @@ env.NanopbProto("unittestproto") # Protocol definitions for basic_buffer/stream tests env.NanopbProto("person") +#-------------------------------------------- # Binaries of the pb_decode.c and pb_encode.c # These are built using more strict warning flags. strict = env.Clone() strict.Append(CFLAGS = strict['CORECFLAGS']) strict.Object("pb_decode.o", "$NANOPB/pb_decode.c") strict.Object("pb_encode.o", "$NANOPB/pb_encode.c") +strict.Object("pb_common.o", "$NANOPB/pb_common.c") + +#----------------------------------------------- +# Binaries of pb_decode etc. with malloc support +# Uses malloc_wrappers.c to count allocations. +malloc_env = env.Clone() +malloc_env.Append(CPPDEFINES = {'PB_ENABLE_MALLOC': 1, + 'PB_SYSTEM_HEADER': '\\"malloc_wrappers_syshdr.h\\"'}) +malloc_env.Append(CPPPATH = ["$COMMON"]) + +if 'SYSHDR' in malloc_env: + malloc_env.Append(CPPDEFINES = {'PB_OLD_SYSHDR': malloc_env['SYSHDR']}) + +# Disable libmudflap, because it will confuse valgrind +# and other memory leak detection tools. +if '-fmudflap' in env["CCFLAGS"]: + malloc_env["CCFLAGS"].remove("-fmudflap") + malloc_env["LINKFLAGS"].remove("-fmudflap") + malloc_env["LIBS"].remove("mudflap") + +malloc_strict = malloc_env.Clone() +malloc_strict.Append(CFLAGS = malloc_strict['CORECFLAGS']) +malloc_strict.Object("pb_decode_with_malloc.o", "$NANOPB/pb_decode.c") +malloc_strict.Object("pb_encode_with_malloc.o", "$NANOPB/pb_encode.c") +malloc_strict.Object("pb_common_with_malloc.o", "$NANOPB/pb_common.c") + +malloc_env.Object("malloc_wrappers.o", "malloc_wrappers.c") +malloc_env.Depends("$NANOPB/pb.h", ["malloc_wrappers_syshdr.h", "malloc_wrappers.h"]) + +Export("malloc_env") diff --git a/tests/common/malloc_wrappers.c b/tests/common/malloc_wrappers.c new file mode 100644 index 0000000..ad69f1c --- /dev/null +++ b/tests/common/malloc_wrappers.c @@ -0,0 +1,54 @@ +#include "malloc_wrappers.h" +#include <stdint.h> +#include <assert.h> +#include <string.h> + +static size_t alloc_count = 0; + +/* Allocate memory and place check values before and after. */ +void* malloc_with_check(size_t size) +{ + size_t size32 = (size + 3) / 4 + 3; + uint32_t *buf = malloc(size32 * sizeof(uint32_t)); + buf[0] = size32; + buf[1] = 0xDEADBEEF; + buf[size32 - 1] = 0xBADBAD; + return buf + 2; +} + +/* Free memory allocated with malloc_with_check() and do the checks. */ +void free_with_check(void *mem) +{ + uint32_t *buf = (uint32_t*)mem - 2; + assert(buf[1] == 0xDEADBEEF); + assert(buf[buf[0] - 1] == 0xBADBAD); + free(buf); +} + +/* Track memory usage */ +void* counting_realloc(void *ptr, size_t size) +{ + /* Don't allocate crazy amounts of RAM when fuzzing */ + if (size > 1000000) + return NULL; + + if (!ptr && size) + alloc_count++; + + return realloc(ptr, size); +} + +void counting_free(void *ptr) +{ + if (ptr) + { + assert(alloc_count > 0); + alloc_count--; + free(ptr); + } +} + +size_t get_alloc_count() +{ + return alloc_count; +} diff --git a/tests/common/malloc_wrappers.h b/tests/common/malloc_wrappers.h new file mode 100644 index 0000000..7eec795 --- /dev/null +++ b/tests/common/malloc_wrappers.h @@ -0,0 +1,7 @@ +#include <stdlib.h> + +void* malloc_with_check(size_t size); +void free_with_check(void *mem); +void* counting_realloc(void *ptr, size_t size); +void counting_free(void *ptr); +size_t get_alloc_count(); diff --git a/tests/common/malloc_wrappers_syshdr.h b/tests/common/malloc_wrappers_syshdr.h new file mode 100644 index 0000000..d295d9e --- /dev/null +++ b/tests/common/malloc_wrappers_syshdr.h @@ -0,0 +1,15 @@ +/* This is just a wrapper in order to get our own malloc wrappers into nanopb core. */ + +#define pb_realloc(ptr,size) counting_realloc(ptr,size) +#define pb_free(ptr) counting_free(ptr) + +#ifdef PB_OLD_SYSHDR +#include PB_OLD_SYSHDR +#else +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <string.h> +#endif + +#include <malloc_wrappers.h> diff --git a/tests/common/person.proto b/tests/common/person.proto index dafcf93..becefdf 100644 --- a/tests/common/person.proto +++ b/tests/common/person.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + import "nanopb.proto"; message Person { diff --git a/tests/common/unittestproto.proto b/tests/common/unittestproto.proto index eb3e7de..23b5b97 100644 --- a/tests/common/unittestproto.proto +++ b/tests/common/unittestproto.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + import 'nanopb.proto'; message IntegerArray { @@ -34,3 +36,8 @@ message CallbackContainer { message CallbackContainerContainer { required CallbackContainer submsg = 1; } + +message StringPointerContainer { + repeated string rep_str = 1 [(nanopb).type = FT_POINTER]; +} + diff --git a/tests/cxx_main_program/SConscript b/tests/cxx_main_program/SConscript index e78c6b3..edb8812 100644 --- a/tests/cxx_main_program/SConscript +++ b/tests/cxx_main_program/SConscript @@ -11,14 +11,15 @@ env.Append(CPPDEFINES = ['__STDC_LIMIT_MACROS']) c = Copy("$TARGET", "$SOURCE") env.Command("pb_encode.cxx", "#../pb_encode.c", c) env.Command("pb_decode.cxx", "#../pb_decode.c", c) +env.Command("pb_common.cxx", "#../pb_common.c", c) env.Command("alltypes.pb.h", "$BUILD/alltypes/alltypes.pb.h", c) env.Command("alltypes.pb.cxx", "$BUILD/alltypes/alltypes.pb.c", c) env.Command("encode_alltypes.cxx", "$BUILD/alltypes/encode_alltypes.c", c) env.Command("decode_alltypes.cxx", "$BUILD/alltypes/decode_alltypes.c", c) # Now build and run the test normally. -enc = env.Program(["encode_alltypes.cxx", "alltypes.pb.cxx", "pb_encode.cxx"]) -dec = env.Program(["decode_alltypes.cxx", "alltypes.pb.cxx", "pb_decode.cxx"]) +enc = env.Program(["encode_alltypes.cxx", "alltypes.pb.cxx", "pb_encode.cxx", "pb_common.cxx"]) +dec = env.Program(["decode_alltypes.cxx", "alltypes.pb.cxx", "pb_decode.cxx", "pb_common.cxx"]) env.RunTest(enc) env.RunTest([dec, "encode_alltypes.output"]) diff --git a/tests/cyclic_messages/SConscript b/tests/cyclic_messages/SConscript new file mode 100644 index 0000000..c782001 --- /dev/null +++ b/tests/cyclic_messages/SConscript @@ -0,0 +1,11 @@ +Import("env") + +# Encode cyclic messages with callback fields + +c = Copy("$TARGET", "$SOURCE") +env.Command("cyclic_callback.proto", "cyclic.proto", c) +env.NanopbProto(["cyclic_callback", "cyclic_callback.options"]) + +enc_callback = env.Program(["encode_cyclic_callback.c", "cyclic_callback.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) + + diff --git a/tests/cyclic_messages/cyclic.proto b/tests/cyclic_messages/cyclic.proto new file mode 100644 index 0000000..8cab0b1 --- /dev/null +++ b/tests/cyclic_messages/cyclic.proto @@ -0,0 +1,27 @@ +// Test structures with cyclic references. +// These can only be handled in pointer/callback mode, +// see associated .options files. + +syntax = "proto2"; + +message TreeNode +{ + optional int32 leaf = 1; + optional TreeNode left = 2; + optional TreeNode right = 3; +} + +message Dictionary +{ + repeated KeyValuePair dictItem = 1; +} + +message KeyValuePair +{ + required string key = 1; + optional string stringValue = 2; + optional int32 intValue = 3; + optional Dictionary dictValue = 4; + optional TreeNode treeValue = 5; +} + diff --git a/tests/cyclic_messages/cyclic_callback.options b/tests/cyclic_messages/cyclic_callback.options new file mode 100644 index 0000000..fd4e1e1 --- /dev/null +++ b/tests/cyclic_messages/cyclic_callback.options @@ -0,0 +1,7 @@ +TreeNode.left type:FT_CALLBACK +TreeNode.right type:FT_CALLBACK + +Dictionary.data type:FT_CALLBACK +KeyValuePair.key max_size:8 +KeyValuePair.stringValue max_size:8 +KeyValuePair.treeValue type:FT_CALLBACK diff --git a/tests/cyclic_messages/encode_cyclic_callback.c b/tests/cyclic_messages/encode_cyclic_callback.c new file mode 100644 index 0000000..7f67e70 --- /dev/null +++ b/tests/cyclic_messages/encode_cyclic_callback.c @@ -0,0 +1,148 @@ +/* This program parses an input string in a format a bit like JSON: + * {'foobar': 1234, 'xyz': 'abc', 'tree': [[[1, 2], 3], [4, 5]]} + * and encodes it as protobuf + * + * Note: The string parsing here is not in any way intended to be robust + * nor safe against buffer overflows. It is just for this test. + */ + +#include <pb_encode.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "cyclic_callback.pb.h" + +static char *find_end_of_item(char *p) +{ + int depth = 0; + do { + if (*p == '[' || *p == '{') depth++; + if (*p == ']' || *p == '}') depth--; + p++; + } while (depth > 0 || (*p != ',' && *p != '}')); + + if (*p == '}') + return p; /* End of parent dict */ + + p++; + while (*p == ' ') p++; + return p; +} + +/* Parse a tree in format [[1 2] 3] and encode it directly to protobuf */ +static bool encode_tree(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + TreeNode tree = TreeNode_init_zero; + char *p = (char*)*arg; + + if (*p == '[') + { + /* This is a tree branch */ + p++; + tree.left.funcs.encode = encode_tree; + tree.left.arg = p; + + p = find_end_of_item(p); + tree.right.funcs.encode = encode_tree; + tree.right.arg = p; + } + else + { + /* This is a leaf node */ + tree.has_leaf = true; + tree.leaf = atoi(p); + } + + return pb_encode_tag_for_field(stream, field) && + pb_encode_submessage(stream, TreeNode_fields, &tree); +} + +/* Parse a dictionary in format {'name': value} and encode it directly to protobuf */ +static bool encode_dictionary(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + int textlen; + char *p = (char*)*arg; + if (*p == '{') p++; + while (*p != '}') + { + KeyValuePair pair = KeyValuePair_init_zero; + + if (*p != '\'') + PB_RETURN_ERROR(stream, "invalid key, missing quote"); + + p++; /* Starting quote of key */ + textlen = strchr(p, '\'') - p; + strncpy(pair.key, p, textlen); + pair.key[textlen] = 0; + p += textlen + 2; + + while (*p == ' ') p++; + + if (*p == '[') + { + /* Value is a tree */ + pair.treeValue.funcs.encode = encode_tree; + pair.treeValue.arg = p; + } + else if (*p == '\'') + { + /* Value is a string */ + pair.has_stringValue = true; + p++; + textlen = strchr(p, '\'') - p; + strncpy(pair.stringValue, p, textlen); + pair.stringValue[textlen] = 0; + } + else if (*p == '{') + { + /* Value is a dictionary */ + pair.has_dictValue = true; + pair.dictValue.dictItem.funcs.encode = encode_dictionary; + pair.dictValue.dictItem.arg = p; + } + else + { + /* Value is integer */ + pair.has_intValue = true; + pair.intValue = atoi(p); + } + + p = find_end_of_item(p); + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + if (!pb_encode_submessage(stream, KeyValuePair_fields, &pair)) + return false; + } + + return true; +} + + +int main(int argc, char *argv[]) +{ + uint8_t buffer[256]; + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + Dictionary dict = Dictionary_init_zero; + + if (argc <= 1) + { + fprintf(stderr, "Usage: %s \"{'foobar': 1234, ...}\"\n", argv[0]); + return 1; + } + + dict.dictItem.funcs.encode = encode_dictionary; + dict.dictItem.arg = argv[1]; + + if (!pb_encode(&stream, Dictionary_fields, &dict)) + { + fprintf(stderr, "Encoding error: %s\n", PB_GET_ERROR(&stream)); + return 1; + } + + fwrite(buffer, 1, stream.bytes_written, stdout); + return 0; +} + + diff --git a/tests/decode_unittests/decode_unittests.c b/tests/decode_unittests/decode_unittests.c index 1be0191..dc1e719 100644 --- a/tests/decode_unittests/decode_unittests.c +++ b/tests/decode_unittests/decode_unittests.c @@ -1,4 +1,6 @@ /* This includes the whole .c file to get access to static functions. */ +#define PB_ENABLE_MALLOC +#include "pb_common.c" #include "pb_decode.c" #include <stdio.h> @@ -85,6 +87,23 @@ int main() pb_decode_varint(&s, (uint64_t*)&i) && i == -1)); TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), pb_decode_varint(&s, &u) && u == UINT64_MAX)); + TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), + !pb_decode_varint(&s, &u))); + } + + { + pb_istream_t s; + uint32_t u; + + COMMENT("Test pb_decode_varint32"); + TEST((s = S("\x00"), pb_decode_varint32(&s, &u) && u == 0)); + TEST((s = S("\x01"), pb_decode_varint32(&s, &u) && u == 1)); + TEST((s = S("\xAC\x02"), pb_decode_varint32(&s, &u) && u == 300)); + TEST((s = S("\xFF\xFF\xFF\xFF\x0F"), pb_decode_varint32(&s, &u) && u == UINT32_MAX)); + TEST((s = S("\xFF\xFF\xFF\xFF\x8F\x00"), pb_decode_varint32(&s, &u) && u == UINT32_MAX)); + TEST((s = S("\xFF\xFF\xFF\xFF\x10"), !pb_decode_varint32(&s, &u))); + TEST((s = S("\xFF\xFF\xFF\xFF\x40"), !pb_decode_varint32(&s, &u))); + TEST((s = S("\xFF\xFF\xFF\xFF\xFF\x01"), !pb_decode_varint32(&s, &u))); } { @@ -107,16 +126,16 @@ int main() } { - pb_istream_t s = S("\x01\xFF\xFF\x03"); + pb_istream_t s = S("\x01\x00"); pb_field_t f = {1, PB_LTYPE_VARINT, 0, 0, 4, 0, 0}; uint32_t d; COMMENT("Test pb_dec_varint using uint32_t") TEST(pb_dec_varint(&s, &f, &d) && d == 1) /* Verify that no more than data_size is written. */ - d = 0; + d = 0xFFFFFFFF; f.data_size = 1; - TEST(pb_dec_varint(&s, &f, &d) && (d == 0xFF || d == 0xFF000000)) + TEST(pb_dec_varint(&s, &f, &d) && (d == 0xFFFFFF00 || d == 0x00FFFFFF)) } { @@ -134,9 +153,9 @@ int main() { pb_istream_t s; pb_field_t f = {1, PB_LTYPE_SVARINT, 0, 0, 8, 0, 0}; - uint64_t d; + int64_t d; - COMMENT("Test pb_dec_svarint using uint64_t") + COMMENT("Test pb_dec_svarint using int64_t") TEST((s = S("\x01"), pb_dec_svarint(&s, &f, &d) && d == -1)) TEST((s = S("\x02"), pb_dec_svarint(&s, &f, &d) && d == 1)) TEST((s = S("\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), pb_dec_svarint(&s, &f, &d) && d == INT64_MAX)) @@ -145,6 +164,50 @@ int main() { pb_istream_t s; + pb_field_t f = {1, PB_LTYPE_SVARINT, 0, 0, 4, 0, 0}; + int32_t d; + + COMMENT("Test pb_dec_svarint overflow detection using int32_t"); + TEST((s = S("\xfe\xff\xff\xff\x0f"), pb_dec_svarint(&s, &f, &d))); + TEST((s = S("\xfe\xff\xff\xff\x10"), !pb_dec_svarint(&s, &f, &d))); + TEST((s = S("\xff\xff\xff\xff\x0f"), pb_dec_svarint(&s, &f, &d))); + TEST((s = S("\xff\xff\xff\xff\x10"), !pb_dec_svarint(&s, &f, &d))); + } + + { + pb_istream_t s; + pb_field_t f = {1, PB_LTYPE_SVARINT, 0, 0, 4, 0, 0}; + uint32_t d; + + COMMENT("Test pb_dec_uvarint using uint32_t") + TEST((s = S("\x01"), pb_dec_uvarint(&s, &f, &d) && d == 1)) + TEST((s = S("\x02"), pb_dec_uvarint(&s, &f, &d) && d == 2)) + TEST((s = S("\xff\xff\xff\xff\x0f"), pb_dec_uvarint(&s, &f, &d) && d == UINT32_MAX)) + } + + { + pb_istream_t s; + pb_field_t f = {1, PB_LTYPE_SVARINT, 0, 0, 8, 0, 0}; + uint64_t d; + + COMMENT("Test pb_dec_uvarint using uint64_t") + TEST((s = S("\x01"), pb_dec_uvarint(&s, &f, &d) && d == 1)) + TEST((s = S("\x02"), pb_dec_uvarint(&s, &f, &d) && d == 2)) + TEST((s = S("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x01"), pb_dec_uvarint(&s, &f, &d) && d == UINT64_MAX)) + } + + { + pb_istream_t s; + pb_field_t f = {1, PB_LTYPE_SVARINT, 0, 0, 4, 0, 0}; + uint32_t d; + + COMMENT("Test pb_dec_uvarint overflow detection using int32_t"); + TEST((s = S("\xff\xff\xff\xff\x0f"), pb_dec_uvarint(&s, &f, &d))); + TEST((s = S("\xff\xff\xff\xff\x10"), !pb_dec_uvarint(&s, &f, &d))); + } + + { + pb_istream_t s; pb_field_t f = {1, PB_LTYPE_FIXED32, 0, 0, 4, 0, 0}; float d; @@ -168,7 +231,7 @@ int main() { pb_istream_t s; - struct { size_t size; uint8_t bytes[5]; } d; + struct { pb_size_t size; uint8_t bytes[5]; } d; pb_field_t f = {1, PB_LTYPE_BYTES, 0, 0, sizeof(d), 0, 0}; COMMENT("Test pb_dec_bytes") @@ -249,7 +312,7 @@ int main() { pb_istream_t s; CallbackArray dest; - struct { size_t size; uint8_t bytes[10]; } ref; + struct { pb_size_t size; uint8_t bytes[10]; } ref; dest.data.funcs.decode = &callback_check; dest.data.arg = &ref; @@ -291,6 +354,14 @@ int main() { pb_istream_t s; + IntegerArray dest; + + COMMENT("Testing pb_decode with invalid tag numbers") + TEST((s = S("\x9f\xea"), !pb_decode(&s, IntegerArray_fields, &dest))); + } + + { + pb_istream_t s; IntegerContainer dest = {{0}}; COMMENT("Testing pb_decode_delimited") @@ -299,6 +370,28 @@ int main() dest.submsg.data_count == 5) } + { + pb_istream_t s = {0}; + void *data = NULL; + + COMMENT("Testing allocate_field") + TEST(allocate_field(&s, &data, 10, 10) && data != NULL); + TEST(allocate_field(&s, &data, 10, 20) && data != NULL); + + { + void *oldvalue = data; + size_t very_big = (size_t)-1; + size_t somewhat_big = very_big / 2 + 1; + size_t not_so_big = (size_t)1 << (4 * sizeof(size_t)); + + TEST(!allocate_field(&s, &data, very_big, 2) && data == oldvalue); + TEST(!allocate_field(&s, &data, somewhat_big, 2) && data == oldvalue); + TEST(!allocate_field(&s, &data, not_so_big, not_so_big) && data == oldvalue); + } + + pb_free(data); + } + if (status != 0) fprintf(stdout, "\n\nSome tests FAILED!\n"); diff --git a/tests/encode_unittests/encode_unittests.c b/tests/encode_unittests/encode_unittests.c index 06935f9..583af5c 100644 --- a/tests/encode_unittests/encode_unittests.c +++ b/tests/encode_unittests/encode_unittests.c @@ -1,4 +1,5 @@ /* This includes the whole .c file to get access to static functions. */ +#include "pb_common.c" #include "pb_encode.c" #include <stdio.h> @@ -169,7 +170,7 @@ int main() { uint8_t buffer[30]; pb_ostream_t s; - struct { size_t size; uint8_t bytes[5]; } value = {5, {'x', 'y', 'z', 'z', 'y'}}; + struct { pb_size_t size; uint8_t bytes[5]; } value = {5, {'x', 'y', 'z', 'z', 'y'}}; COMMENT("Test pb_enc_bytes") TEST(WRITES(pb_enc_bytes(&s, &BytesMessage_fields[0], &value), "\x05xyzzy")) @@ -330,6 +331,23 @@ int main() TEST(s.bytes_written == StringMessage_size); } + { + uint8_t buffer[128]; + pb_ostream_t s; + StringPointerContainer msg = StringPointerContainer_init_zero; + char *strs[1] = {NULL}; + char zstr[] = "Z"; + + COMMENT("Test string pointer encoding."); + + msg.rep_str = strs; + msg.rep_str_count = 1; + TEST(WRITES(pb_encode(&s, StringPointerContainer_fields, &msg), "\x0a\x00")) + + strs[0] = zstr; + TEST(WRITES(pb_encode(&s, StringPointerContainer_fields, &msg), "\x0a\x01Z")) + } + if (status != 0) fprintf(stdout, "\n\nSome tests FAILED!\n"); diff --git a/tests/enum_sizes/SConscript b/tests/enum_sizes/SConscript new file mode 100644 index 0000000..048592e --- /dev/null +++ b/tests/enum_sizes/SConscript @@ -0,0 +1,12 @@ +# Test that different sizes of enum fields are properly encoded and decoded. + +Import('env') + +env.NanopbProto('enumsizes') + +p = env.Program(["enumsizes_unittests.c", + "enumsizes.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_decode.o", + "$COMMON/pb_common.o"]) +env.RunTest(p) diff --git a/tests/enum_sizes/enumsizes.proto b/tests/enum_sizes/enumsizes.proto new file mode 100644 index 0000000..a85d416 --- /dev/null +++ b/tests/enum_sizes/enumsizes.proto @@ -0,0 +1,86 @@ +/* Test handling of enums with different value ranges. + * Depending on compiler and the packed_enum setting, the datatypes + * for enums can be either signed or unsigned. In past this has caused + * a bit of a problem for the encoder/decoder (issue #164). + */ + +syntax = "proto2"; + +import 'nanopb.proto'; + +option (nanopb_fileopt).long_names = false; + +enum UnpackedUint8 { + option (nanopb_enumopt).packed_enum = false; + UU8_MIN = 0; + UU8_MAX = 255; +} + +enum PackedUint8 { + option (nanopb_enumopt).packed_enum = true; + PU8_MIN = 0; + PU8_MAX = 255; +} + +enum UnpackedInt8 { + option (nanopb_enumopt).packed_enum = false; + UI8_MIN = -128; + UI8_MAX = 127; +} + +enum PackedInt8 { + option (nanopb_enumopt).packed_enum = true; + PI8_MIN = -128; + PI8_MAX = 127; +} + +enum UnpackedUint16 { + option (nanopb_enumopt).packed_enum = false; + UU16_MIN = 0; + UU16_MAX = 65535; +} + +enum PackedUint16 { + option (nanopb_enumopt).packed_enum = true; + PU16_MIN = 0; + PU16_MAX = 65535; +} + +enum UnpackedInt16 { + option (nanopb_enumopt).packed_enum = false; + UI16_MIN = -32768; + UI16_MAX = 32767; +} + +enum PackedInt16 { + option (nanopb_enumopt).packed_enum = true; + PI16_MIN = -32768; + PI16_MAX = 32767; +} + +/* Protobuf supports enums up to 32 bits. + * The 32 bit case is covered by HugeEnum in the alltypes test. + */ + +message PackedEnums { + required PackedUint8 u8_min = 1; + required PackedUint8 u8_max = 2; + required PackedInt8 i8_min = 3; + required PackedInt8 i8_max = 4; + required PackedUint16 u16_min = 5; + required PackedUint16 u16_max = 6; + required PackedInt16 i16_min = 7; + required PackedInt16 i16_max = 8; +} + +message UnpackedEnums { + required UnpackedUint8 u8_min = 1; + required UnpackedUint8 u8_max = 2; + required UnpackedInt8 i8_min = 3; + required UnpackedInt8 i8_max = 4; + required UnpackedUint16 u16_min = 5; + required UnpackedUint16 u16_max = 6; + required UnpackedInt16 i16_min = 7; + required UnpackedInt16 i16_max = 8; +} + diff --git a/tests/enum_sizes/enumsizes_unittests.c b/tests/enum_sizes/enumsizes_unittests.c new file mode 100644 index 0000000..5606895 --- /dev/null +++ b/tests/enum_sizes/enumsizes_unittests.c @@ -0,0 +1,72 @@ +#include <stdio.h> +#include <string.h> +#include <pb_decode.h> +#include <pb_encode.h> +#include "unittests.h" +#include "enumsizes.pb.h" + +int main() +{ + int status = 0; + + UnpackedEnums msg1 = { + UU8_MIN, UU8_MAX, + UI8_MIN, UI8_MAX, + UU16_MIN, UU16_MAX, + UI16_MIN, UI16_MAX, + }; + + PackedEnums msg2; + UnpackedEnums msg3; + uint8_t buf[256]; + size_t msgsize; + + COMMENT("Step 1: unpacked enums -> protobuf"); + { + pb_ostream_t s = pb_ostream_from_buffer(buf, sizeof(buf)); + TEST(pb_encode(&s, UnpackedEnums_fields, &msg1)); + msgsize = s.bytes_written; + } + + COMMENT("Step 2: protobuf -> packed enums"); + { + pb_istream_t s = pb_istream_from_buffer(buf, msgsize); + TEST(pb_decode(&s, PackedEnums_fields, &msg2)); + + TEST(msg1.u8_min == (int)msg2.u8_min); + TEST(msg1.u8_max == (int)msg2.u8_max); + TEST(msg1.i8_min == (int)msg2.i8_min); + TEST(msg1.i8_max == (int)msg2.i8_max); + TEST(msg1.u16_min == (int)msg2.u16_min); + TEST(msg1.u16_max == (int)msg2.u16_max); + TEST(msg1.i16_min == (int)msg2.i16_min); + TEST(msg1.i16_max == (int)msg2.i16_max); + } + + COMMENT("Step 3: packed enums -> protobuf"); + { + pb_ostream_t s = pb_ostream_from_buffer(buf, sizeof(buf)); + TEST(pb_encode(&s, PackedEnums_fields, &msg2)); + msgsize = s.bytes_written; + } + + COMMENT("Step 4: protobuf -> unpacked enums"); + { + pb_istream_t s = pb_istream_from_buffer(buf, msgsize); + TEST(pb_decode(&s, UnpackedEnums_fields, &msg3)); + + TEST(msg1.u8_min == (int)msg3.u8_min); + TEST(msg1.u8_max == (int)msg3.u8_max); + TEST(msg1.i8_min == (int)msg3.i8_min); + TEST(msg1.i8_max == (int)msg3.i8_max); + TEST(msg1.u16_min == (int)msg2.u16_min); + TEST(msg1.u16_max == (int)msg2.u16_max); + TEST(msg1.i16_min == (int)msg2.i16_min); + TEST(msg1.i16_max == (int)msg2.i16_max); + } + + if (status != 0) + fprintf(stdout, "\n\nSome tests FAILED!\n"); + + return status; +} diff --git a/tests/enum_to_string/SConscript b/tests/enum_to_string/SConscript new file mode 100644 index 0000000..e86fcca --- /dev/null +++ b/tests/enum_to_string/SConscript @@ -0,0 +1,7 @@ +# Test enum to string functionality + +Import('env') +env.NanopbProto("enum.proto") +p = env.Program(["enum_to_string.c", "enum.pb.c"]) +env.RunTest(p) + diff --git a/tests/enum_to_string/enum.proto b/tests/enum_to_string/enum.proto new file mode 100644 index 0000000..07c6736 --- /dev/null +++ b/tests/enum_to_string/enum.proto @@ -0,0 +1,19 @@ +/* Test enum to string function generation */ + +syntax = "proto2"; + +import "nanopb.proto"; + +option (nanopb_fileopt).enum_to_string = true; + +enum MyEnum { + VALUE1 = 1; + VALUE2 = 2; + VALUE15 = 15; +} + +enum MyShortNameEnum { + option (nanopb_enumopt).long_names = false; + MSNE_VALUE256 = 256; +} + diff --git a/tests/enum_to_string/enum_to_string.c b/tests/enum_to_string/enum_to_string.c new file mode 100644 index 0000000..c4fb31d --- /dev/null +++ b/tests/enum_to_string/enum_to_string.c @@ -0,0 +1,19 @@ +#include <stdio.h> +#include "unittests.h" +#include "enum.pb.h" + +int main() +{ + int status = 0; + TEST(strcmp(MyEnum_name(MyEnum_VALUE1), "VALUE1") == 0); + TEST(strcmp(MyEnum_name(MyEnum_VALUE2), "VALUE2") == 0); + TEST(strcmp(MyEnum_name(MyEnum_VALUE15), "VALUE15") == 0); + TEST(strcmp(MyShortNameEnum_name(MSNE_VALUE256), "MSNE_VALUE256") == 0); + TEST(strcmp(MyShortNameEnum_name(9999), "unknown") == 0); + + if (status != 0) + fprintf(stdout, "\n\nSome tests FAILED!\n"); + + return status; +} + diff --git a/tests/extensions/SConscript b/tests/extensions/SConscript index 26fc5a3..a2c8742 100644 --- a/tests/extensions/SConscript +++ b/tests/extensions/SConscript @@ -8,8 +8,8 @@ incpath.Append(PROTOCPATH = '$BUILD/alltypes') incpath.Append(CPPPATH = '$BUILD/alltypes') incpath.NanopbProto(["extensions", "extensions.options"]) -enc = incpath.Program(["encode_extensions.c", "extensions.pb.c", "$BUILD/alltypes/alltypes.pb$OBJSUFFIX", "$COMMON/pb_encode.o"]) -dec = incpath.Program(["decode_extensions.c", "extensions.pb.c", "$BUILD/alltypes/alltypes.pb$OBJSUFFIX", "$COMMON/pb_decode.o"]) +enc = incpath.Program(["encode_extensions.c", "extensions.pb.c", "$BUILD/alltypes/alltypes.pb$OBJSUFFIX", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) +dec = incpath.Program(["decode_extensions.c", "extensions.pb.c", "$BUILD/alltypes/alltypes.pb$OBJSUFFIX", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) env.RunTest(enc) env.RunTest([dec, "encode_extensions.output"]) diff --git a/tests/extensions/extensions.proto b/tests/extensions/extensions.proto index da8432e..fcd5b43 100644 --- a/tests/extensions/extensions.proto +++ b/tests/extensions/extensions.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + import 'alltypes.proto'; extend AllTypes { @@ -7,8 +9,8 @@ extend AllTypes { message ExtensionMessage { extend AllTypes { optional ExtensionMessage AllTypes_extensionfield2 = 254; - required ExtensionMessage AllTypes_extensionfield3 = 253; - repeated ExtensionMessage AllTypes_extensionfield4 = 252; + // required ExtensionMessage AllTypes_extensionfield3 = 253; // No longer allowed by protobuf 3 + repeated ExtensionMessage AllTypes_extensionfield4 = 252; } required string test1 = 1; diff --git a/tests/field_size_16/SConscript b/tests/field_size_16/SConscript index 8fee004..99bc344 100644 --- a/tests/field_size_16/SConscript +++ b/tests/field_size_16/SConscript @@ -19,10 +19,14 @@ strict = opts.Clone() strict.Append(CFLAGS = strict['CORECFLAGS']) strict.Object("pb_decode_fields16.o", "$NANOPB/pb_decode.c") strict.Object("pb_encode_fields16.o", "$NANOPB/pb_encode.c") +strict.Object("pb_common_fields16.o", "$NANOPB/pb_common.c") # Now build and run the test normally. -enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_fields16.o"]) -dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_fields16.o"]) +enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_fields16.o", "pb_common_fields16.o"]) +dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_fields16.o", "pb_common_fields16.o"]) env.RunTest(enc) env.RunTest([dec, "encode_alltypes.output"]) + +env.RunTest("optionals.output", enc, ARGS = ['1']) +env.RunTest("optionals.decout", [dec, "optionals.output"], ARGS = ['1']) diff --git a/tests/field_size_16/alltypes.options b/tests/field_size_16/alltypes.options index b31e3cf..78dd08d 100644 --- a/tests/field_size_16/alltypes.options +++ b/tests/field_size_16/alltypes.options @@ -1,3 +1,4 @@ * max_size:16 * max_count:5 +*.*fbytes fixed_length:true max_size:4 diff --git a/tests/field_size_16/alltypes.proto b/tests/field_size_16/alltypes.proto index 7494853..4e27059 100644 --- a/tests/field_size_16/alltypes.proto +++ b/tests/field_size_16/alltypes.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + message SubMessage { required string substuff1 = 1 [default = "1"]; required int32 substuff2 = 2 [default = 2]; @@ -55,7 +57,7 @@ message AllTypes { required SubMessage req_submsg = 16; required MyEnum req_enum = 17; required EmptyMessage req_emptymsg = 18; - + required bytes req_fbytes = 19; repeated int32 rep_int32 = 21; repeated int64 rep_int64 = 22; @@ -78,6 +80,7 @@ message AllTypes { repeated SubMessage rep_submsg = 10036; repeated MyEnum rep_enum = 10037; repeated EmptyMessage rep_emptymsg = 10038; + repeated bytes rep_fbytes = 10039; optional int32 opt_int32 = 10041 [default = 4041]; optional int64 opt_int64 = 10042 [default = 4042]; @@ -96,10 +99,17 @@ message AllTypes { optional double opt_double = 10053 [default = 4053]; optional string opt_string = 10054 [default = "4054"]; - optional bytes opt_bytes = 10055 [default = "4055"]; + optional bytes opt_bytes = 10055 [default = "\x34\x5C\x00\xff"]; optional SubMessage opt_submsg = 10056; optional MyEnum opt_enum = 10057 [default = Second]; optional EmptyMessage opt_emptymsg = 10058; + optional bytes opt_fbytes = 10059 [default = "4059"]; + + oneof oneof + { + SubMessage oneof_msg1 = 10060; + EmptyMessage oneof_msg2 = 10061; + } // Check that extreme integer values are handled correctly required Limits req_limits = 98; @@ -107,5 +117,7 @@ message AllTypes { // Just to make sure that the size of the fields has been calculated // properly, i.e. otherwise a bug in last field might not be detected. required int32 end = 10099; + + extensions 200 to 255; } diff --git a/tests/field_size_16_proto3/SConscript b/tests/field_size_16_proto3/SConscript new file mode 100644 index 0000000..4a8e16d --- /dev/null +++ b/tests/field_size_16_proto3/SConscript @@ -0,0 +1,34 @@ +# Version of AllTypes test case for protobuf 3 file format. + +Import("env") + +import re +match = None +if 'PROTOC_VERSION' in env: + match = re.search('([0-9]+).([0-9]+).([0-9]+)', env['PROTOC_VERSION']) + +if match: + version = list(map(int, match.groups())) + +# proto3 syntax is supported by protoc >= 3.0.0 +if env.GetOption('clean') or (match and version[0] >= 3): + + env.NanopbProto(["alltypes", "alltypes.options"]) + + # Define the compilation options + opts = env.Clone() + opts.Append(CPPDEFINES = {'PB_FIELD_16BIT': 1}) + + # Build new version of core + strict = opts.Clone() + strict.Append(CFLAGS = strict['CORECFLAGS']) + strict.Object("pb_decode_fields16.o", "$NANOPB/pb_decode.c") + strict.Object("pb_encode_fields16.o", "$NANOPB/pb_encode.c") + strict.Object("pb_common_fields16.o", "$NANOPB/pb_common.c") + + # Now build and run the test normally. + enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_fields16.o", "pb_common_fields16.o"]) + dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_fields16.o", "pb_common_fields16.o"]) + + env.RunTest(enc) + env.RunTest([dec, "encode_alltypes.output"]) diff --git a/tests/field_size_16_proto3/alltypes.options b/tests/field_size_16_proto3/alltypes.options new file mode 100644 index 0000000..edfbe78 --- /dev/null +++ b/tests/field_size_16_proto3/alltypes.options @@ -0,0 +1,4 @@ +* max_size:16 +* max_count:5 +*.*fbytes fixed_length:true max_size:4 +SubMessage.substuff1 max_size:256 diff --git a/tests/field_size_16_proto3/alltypes.proto b/tests/field_size_16_proto3/alltypes.proto new file mode 100644 index 0000000..f66109e --- /dev/null +++ b/tests/field_size_16_proto3/alltypes.proto @@ -0,0 +1,100 @@ +syntax = "proto3"; +// package name placeholder + +message SubMessage { + string substuff1 = 1; + int32 substuff2 = 2; + fixed32 substuff3 = 3; +} + +message EmptyMessage { + +} + +enum HugeEnum { + HE_Zero = 0; + Negative = -2147483647; /* protoc doesn't accept -2147483648 here */ + Positive = 2147483647; +} + +message Limits { + int32 int32_min = 1; + int32 int32_max = 2; + uint32 uint32_min = 3; + uint32 uint32_max = 4; + int64 int64_min = 5; + int64 int64_max = 6; + uint64 uint64_min = 7; + uint64 uint64_max = 8; + HugeEnum enum_min = 9; + HugeEnum enum_max = 10; +} + +enum MyEnum { + Zero = 0; + First = 1; + Second = 2; + Truth = 42; +} + +message AllTypes { + int32 sng_int32 = 1; + int64 sng_int64 = 2; + uint32 sng_uint32 = 3; + uint64 sng_uint64 = 4; + sint32 sng_sint32 = 5; + sint64 sng_sint64 = 6; + bool sng_bool = 7; + + fixed32 sng_fixed32 = 8; + sfixed32 sng_sfixed32= 9; + float sng_float = 10; + + fixed64 sng_fixed64 = 11; + sfixed64 sng_sfixed64= 12; + double sng_double = 13; + + string sng_string = 14; + bytes sng_bytes = 15; + SubMessage sng_submsg = 16; + MyEnum sng_enum = 17; + EmptyMessage sng_emptymsg = 18; + bytes sng_fbytes = 19; + + repeated int32 rep_int32 = 21 [packed = true]; + repeated int64 rep_int64 = 22 [packed = true]; + repeated uint32 rep_uint32 = 23 [packed = true]; + repeated uint64 rep_uint64 = 24 [packed = true]; + repeated sint32 rep_sint32 = 25 [packed = true]; + repeated sint64 rep_sint64 = 26 [packed = true]; + repeated bool rep_bool = 27 [packed = true]; + + repeated fixed32 rep_fixed32 = 28 [packed = true]; + repeated sfixed32 rep_sfixed32= 29 [packed = true]; + repeated float rep_float = 30 [packed = true]; + + repeated fixed64 rep_fixed64 = 31 [packed = true]; + repeated sfixed64 rep_sfixed64= 32 [packed = true]; + repeated double rep_double = 33 [packed = true]; + + repeated string rep_string = 34; + repeated bytes rep_bytes = 35; + repeated SubMessage rep_submsg = 36; + repeated MyEnum rep_enum = 37 [packed = true]; + repeated EmptyMessage rep_emptymsg = 38; + repeated bytes rep_fbytes = 39; + + oneof oneof + { + SubMessage oneof_msg1 = 59; + EmptyMessage oneof_msg2 = 60; + } + + // Check that extreme integer values are handled correctly + Limits req_limits = 98; + + // Just to make sure that the size of the fields has been calculated + // properly, i.e. otherwise a bug in last field might not be detected. + int32 end = 99; +} + diff --git a/tests/field_size_16_proto3/decode_alltypes.c b/tests/field_size_16_proto3/decode_alltypes.c new file mode 100644 index 0000000..6611f8c --- /dev/null +++ b/tests/field_size_16_proto3/decode_alltypes.c @@ -0,0 +1,167 @@ +/* Tests the decoding of all types. + * This is the counterpart of test_encode3. + * Run e.g. ./test_encode3 | ./test_decode3 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <pb_decode.h> +#include "alltypes.pb.h" +#include "test_helpers.h" + +#define TEST(x) if (!(x)) { \ + printf("Test " #x " failed.\n"); \ + return false; \ + } + +/* This function is called once from main(), it handles + the decoding and checks the fields. */ +bool check_alltypes(pb_istream_t *stream, int mode) +{ + AllTypes alltypes = AllTypes_init_zero; + + /* Fill with garbage to better detect initialization errors */ + memset(&alltypes, 0xAA, sizeof(alltypes)); + + if (!pb_decode(stream, AllTypes_fields, &alltypes)) + return false; + + TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0); + TEST(alltypes.rep_int64_count == 5 && alltypes.rep_int64[4] == -2002 && alltypes.rep_int64[0] == 0); + TEST(alltypes.rep_uint32_count == 5 && alltypes.rep_uint32[4] == 2003 && alltypes.rep_uint32[0] == 0); + TEST(alltypes.rep_uint64_count == 5 && alltypes.rep_uint64[4] == 2004 && alltypes.rep_uint64[0] == 0); + TEST(alltypes.rep_sint32_count == 5 && alltypes.rep_sint32[4] == -2005 && alltypes.rep_sint32[0] == 0); + TEST(alltypes.rep_sint64_count == 5 && alltypes.rep_sint64[4] == -2006 && alltypes.rep_sint64[0] == 0); + TEST(alltypes.rep_bool_count == 5 && alltypes.rep_bool[4] == true && alltypes.rep_bool[0] == false); + + TEST(alltypes.rep_fixed32_count == 5 && alltypes.rep_fixed32[4] == 2008 && alltypes.rep_fixed32[0] == 0); + TEST(alltypes.rep_sfixed32_count == 5 && alltypes.rep_sfixed32[4] == -2009 && alltypes.rep_sfixed32[0] == 0); + TEST(alltypes.rep_float_count == 5 && alltypes.rep_float[4] == 2010.0f && alltypes.rep_float[0] == 0.0f); + + TEST(alltypes.rep_fixed64_count == 5 && alltypes.rep_fixed64[4] == 2011 && alltypes.rep_fixed64[0] == 0); + TEST(alltypes.rep_sfixed64_count == 5 && alltypes.rep_sfixed64[4] == -2012 && alltypes.rep_sfixed64[0] == 0); + TEST(alltypes.rep_double_count == 5 && alltypes.rep_double[4] == 2013.0 && alltypes.rep_double[0] == 0.0); + + TEST(alltypes.rep_string_count == 5 && strcmp(alltypes.rep_string[4], "2014") == 0 && alltypes.rep_string[0][0] == '\0'); + TEST(alltypes.rep_bytes_count == 5 && alltypes.rep_bytes[4].size == 4 && alltypes.rep_bytes[0].size == 0); + TEST(memcmp(alltypes.rep_bytes[4].bytes, "2015", 4) == 0); + + TEST(alltypes.rep_submsg_count == 5); + TEST(strcmp(alltypes.rep_submsg[4].substuff1, "2016") == 0 && alltypes.rep_submsg[0].substuff1[0] == '\0'); + TEST(alltypes.rep_submsg[4].substuff2 == 2016 && alltypes.rep_submsg[0].substuff2 == 0); + TEST(alltypes.rep_submsg[4].substuff3 == 2016 && alltypes.rep_submsg[0].substuff3 == 0); + + TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero); + TEST(alltypes.rep_emptymsg_count == 5); + + TEST(alltypes.rep_fbytes_count == 5); + TEST(alltypes.rep_fbytes[0][0] == 0 && alltypes.rep_fbytes[0][3] == 0); + TEST(memcmp(alltypes.rep_fbytes[4], "2019", 4) == 0); + + if (mode == 0) + { + /* Expect default values */ + TEST(alltypes.sng_int32 == 0); + TEST(alltypes.sng_int64 == 0); + TEST(alltypes.sng_uint32 == 0); + TEST(alltypes.sng_uint64 == 0); + TEST(alltypes.sng_sint32 == 0); + TEST(alltypes.sng_sint64 == 0); + TEST(alltypes.sng_bool == false); + + TEST(alltypes.sng_fixed32 == 0); + TEST(alltypes.sng_sfixed32 == 0); + TEST(alltypes.sng_float == 0.0f); + + TEST(alltypes.sng_fixed64 == 0); + TEST(alltypes.sng_sfixed64 == 0); + TEST(alltypes.sng_double == 0.0); + + TEST(strcmp(alltypes.sng_string, "") == 0); + TEST(alltypes.sng_bytes.size == 0); + TEST(strcmp(alltypes.sng_submsg.substuff1, "") == 0); + TEST(alltypes.sng_submsg.substuff2 == 0); + TEST(alltypes.sng_submsg.substuff3 == 0); + TEST(alltypes.sng_enum == MyEnum_Zero); + TEST(alltypes.sng_fbytes[0] == 0 && + alltypes.sng_fbytes[1] == 0 && + alltypes.sng_fbytes[2] == 0 && + alltypes.sng_fbytes[3] == 0); + + TEST(alltypes.which_oneof == 0); + } + else + { + /* Expect filled-in values */ + TEST(alltypes.sng_int32 == 3041); + TEST(alltypes.sng_int64 == 3042); + TEST(alltypes.sng_uint32 == 3043); + TEST(alltypes.sng_uint64 == 3044); + TEST(alltypes.sng_sint32 == 3045); + TEST(alltypes.sng_sint64 == 3046); + TEST(alltypes.sng_bool == true); + + TEST(alltypes.sng_fixed32 == 3048); + TEST(alltypes.sng_sfixed32 == 3049); + TEST(alltypes.sng_float == 3050.0f); + + TEST(alltypes.sng_fixed64 == 3051); + TEST(alltypes.sng_sfixed64 == 3052); + TEST(alltypes.sng_double == 3053.0); + + TEST(strcmp(alltypes.sng_string, "3054") == 0); + TEST(alltypes.sng_bytes.size == 4); + TEST(memcmp(alltypes.sng_bytes.bytes, "3055", 4) == 0); + TEST(strcmp(alltypes.sng_submsg.substuff1, "3056") == 0); + TEST(alltypes.sng_submsg.substuff2 == 3056); + TEST(alltypes.sng_submsg.substuff3 == 0); + TEST(alltypes.sng_enum == MyEnum_Truth); + TEST(memcmp(alltypes.sng_fbytes, "3059", 4) == 0); + + TEST(alltypes.which_oneof == AllTypes_oneof_msg1_tag); + TEST(strcmp(alltypes.oneof.oneof_msg1.substuff1, "4059") == 0); + TEST(alltypes.oneof.oneof_msg1.substuff2 == 4059); + } + + TEST(alltypes.req_limits.int32_min == INT32_MIN); + TEST(alltypes.req_limits.int32_max == INT32_MAX); + TEST(alltypes.req_limits.uint32_min == 0); + TEST(alltypes.req_limits.uint32_max == UINT32_MAX); + TEST(alltypes.req_limits.int64_min == INT64_MIN); + TEST(alltypes.req_limits.int64_max == INT64_MAX); + TEST(alltypes.req_limits.uint64_min == 0); + TEST(alltypes.req_limits.uint64_max == UINT64_MAX); + TEST(alltypes.req_limits.enum_min == HugeEnum_Negative); + TEST(alltypes.req_limits.enum_max == HugeEnum_Positive); + + TEST(alltypes.end == 1099); + + return true; +} + +int main(int argc, char **argv) +{ + uint8_t buffer[2048]; + size_t count; + pb_istream_t stream; + + /* Whether to expect the optional values or the default values. */ + int mode = (argc > 1) ? atoi(argv[1]) : 0; + + /* Read the data into buffer */ + SET_BINARY_MODE(stdin); + count = fread(buffer, 1, sizeof(buffer), stdin); + + /* Construct a pb_istream_t for reading from the buffer */ + stream = pb_istream_from_buffer(buffer, count); + + /* Decode and print out the stuff */ + if (!check_alltypes(&stream, mode)) + { + printf("Parsing failed: %s\n", PB_GET_ERROR(&stream)); + return 1; + } else { + return 0; + } +} diff --git a/tests/field_size_16_proto3/encode_alltypes.c b/tests/field_size_16_proto3/encode_alltypes.c new file mode 100644 index 0000000..1da0668 --- /dev/null +++ b/tests/field_size_16_proto3/encode_alltypes.c @@ -0,0 +1,111 @@ +/* Attempts to test all the datatypes supported by ProtoBuf3. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pb_encode.h> +#include "alltypes.pb.h" +#include "test_helpers.h" + +int main(int argc, char **argv) +{ + int mode = (argc > 1) ? atoi(argv[1]) : 0; + + /* Initialize the structure with constants */ + AllTypes alltypes = AllTypes_init_zero; + + alltypes.rep_int32_count = 5; alltypes.rep_int32[4] = -2001; + alltypes.rep_int64_count = 5; alltypes.rep_int64[4] = -2002; + alltypes.rep_uint32_count = 5; alltypes.rep_uint32[4] = 2003; + alltypes.rep_uint64_count = 5; alltypes.rep_uint64[4] = 2004; + alltypes.rep_sint32_count = 5; alltypes.rep_sint32[4] = -2005; + alltypes.rep_sint64_count = 5; alltypes.rep_sint64[4] = -2006; + alltypes.rep_bool_count = 5; alltypes.rep_bool[4] = true; + + alltypes.rep_fixed32_count = 5; alltypes.rep_fixed32[4] = 2008; + alltypes.rep_sfixed32_count = 5; alltypes.rep_sfixed32[4] = -2009; + alltypes.rep_float_count = 5; alltypes.rep_float[4] = 2010.0f; + + alltypes.rep_fixed64_count = 5; alltypes.rep_fixed64[4] = 2011; + alltypes.rep_sfixed64_count = 5; alltypes.rep_sfixed64[4] = -2012; + alltypes.rep_double_count = 5; alltypes.rep_double[4] = 2013.0; + + alltypes.rep_string_count = 5; strcpy(alltypes.rep_string[4], "2014"); + alltypes.rep_bytes_count = 5; alltypes.rep_bytes[4].size = 4; + memcpy(alltypes.rep_bytes[4].bytes, "2015", 4); + + alltypes.rep_submsg_count = 5; + strcpy(alltypes.rep_submsg[4].substuff1, "2016"); + alltypes.rep_submsg[4].substuff2 = 2016; + alltypes.rep_submsg[4].substuff3 = 2016; + + alltypes.rep_enum_count = 5; alltypes.rep_enum[4] = MyEnum_Truth; + alltypes.rep_emptymsg_count = 5; + + alltypes.rep_fbytes_count = 5; + memcpy(alltypes.rep_fbytes[4], "2019", 4); + + alltypes.req_limits.int32_min = INT32_MIN; + alltypes.req_limits.int32_max = INT32_MAX; + alltypes.req_limits.uint32_min = 0; + alltypes.req_limits.uint32_max = UINT32_MAX; + alltypes.req_limits.int64_min = INT64_MIN; + alltypes.req_limits.int64_max = INT64_MAX; + alltypes.req_limits.uint64_min = 0; + alltypes.req_limits.uint64_max = UINT64_MAX; + alltypes.req_limits.enum_min = HugeEnum_Negative; + alltypes.req_limits.enum_max = HugeEnum_Positive; + + if (mode != 0) + { + /* Fill in values for singular fields */ + alltypes.sng_int32 = 3041; + alltypes.sng_int64 = 3042; + alltypes.sng_uint32 = 3043; + alltypes.sng_uint64 = 3044; + alltypes.sng_sint32 = 3045; + alltypes.sng_sint64 = 3046; + alltypes.sng_bool = true; + + alltypes.sng_fixed32 = 3048; + alltypes.sng_sfixed32 = 3049; + alltypes.sng_float = 3050.0f; + + alltypes.sng_fixed64 = 3051; + alltypes.sng_sfixed64 = 3052; + alltypes.sng_double = 3053.0; + + strcpy(alltypes.sng_string, "3054"); + alltypes.sng_bytes.size = 4; + memcpy(alltypes.sng_bytes.bytes, "3055", 4); + strcpy(alltypes.sng_submsg.substuff1, "3056"); + alltypes.sng_submsg.substuff2 = 3056; + alltypes.sng_enum = MyEnum_Truth; + memcpy(alltypes.sng_fbytes, "3059", 4); + + alltypes.which_oneof = AllTypes_oneof_msg1_tag; + strcpy(alltypes.oneof.oneof_msg1.substuff1, "4059"); + alltypes.oneof.oneof_msg1.substuff2 = 4059; + } + + alltypes.end = 1099; + + { + uint8_t buffer[AllTypes_size]; + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + + /* Now encode it and check if we succeeded. */ + if (pb_encode(&stream, AllTypes_fields, &alltypes)) + { + SET_BINARY_MODE(stdout); + fwrite(buffer, 1, stream.bytes_written, stdout); + return 0; /* Success */ + } + else + { + fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream)); + return 1; /* Failure */ + } + } +} diff --git a/tests/field_size_32/SConscript b/tests/field_size_32/SConscript index 2a64c6c..650c626 100644 --- a/tests/field_size_32/SConscript +++ b/tests/field_size_32/SConscript @@ -19,10 +19,14 @@ strict = opts.Clone() strict.Append(CFLAGS = strict['CORECFLAGS']) strict.Object("pb_decode_fields32.o", "$NANOPB/pb_decode.c") strict.Object("pb_encode_fields32.o", "$NANOPB/pb_encode.c") +strict.Object("pb_common_fields32.o", "$NANOPB/pb_common.c") # Now build and run the test normally. -enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_fields32.o"]) -dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_fields32.o"]) +enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_fields32.o", "pb_common_fields32.o"]) +dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_fields32.o", "pb_common_fields32.o"]) env.RunTest(enc) env.RunTest([dec, "encode_alltypes.output"]) + +env.RunTest("optionals.output", enc, ARGS = ['1']) +env.RunTest("optionals.decout", [dec, "optionals.output"], ARGS = ['1']) diff --git a/tests/field_size_32/alltypes.options b/tests/field_size_32/alltypes.options index b31e3cf..0d5ab12 100644 --- a/tests/field_size_32/alltypes.options +++ b/tests/field_size_32/alltypes.options @@ -1,3 +1,3 @@ * max_size:16 * max_count:5 - +*.*fbytes fixed_length:true max_size:4 diff --git a/tests/field_size_32/alltypes.proto b/tests/field_size_32/alltypes.proto index 17f17ee..a05e3b9 100644 --- a/tests/field_size_32/alltypes.proto +++ b/tests/field_size_32/alltypes.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + message SubMessage { required string substuff1 = 1 [default = "1"]; required int32 substuff2 = 2 [default = 2]; @@ -55,7 +57,7 @@ message AllTypes { required SubMessage req_submsg = 16; required MyEnum req_enum = 17; required EmptyMessage req_emptymsg = 18; - + required bytes req_fbytes = 19; repeated int32 rep_int32 = 21; repeated int64 rep_int64 = 22; @@ -78,6 +80,7 @@ message AllTypes { repeated SubMessage rep_submsg = 10036; repeated MyEnum rep_enum = 10037; repeated EmptyMessage rep_emptymsg = 10038; + repeated bytes rep_fbytes = 10039; optional int32 opt_int32 = 10041 [default = 4041]; optional int64 opt_int64 = 10042 [default = 4042]; @@ -96,10 +99,17 @@ message AllTypes { optional double opt_double = 10053 [default = 4053]; optional string opt_string = 10054 [default = "4054"]; - optional bytes opt_bytes = 10055 [default = "4055"]; + optional bytes opt_bytes = 10055 [default = "\x34\x5C\x00\xff"]; optional SubMessage opt_submsg = 10056; optional MyEnum opt_enum = 10057 [default = Second]; optional EmptyMessage opt_emptymsg = 10058; + optional bytes opt_fbytes = 10059 [default = "4059"]; + + oneof oneof + { + SubMessage oneof_msg1 = 10060; + EmptyMessage oneof_msg2 = 10061; + } // Check that extreme integer values are handled correctly required Limits req_limits = 98; @@ -107,5 +117,7 @@ message AllTypes { // Just to make sure that the size of the fields has been calculated // properly, i.e. otherwise a bug in last field might not be detected. required int32 end = 13432099; + + extensions 200 to 255; } diff --git a/tests/fixed_count/SConscript b/tests/fixed_count/SConscript new file mode 100644 index 0000000..3cecb12 --- /dev/null +++ b/tests/fixed_count/SConscript @@ -0,0 +1,14 @@ +# Test that fixed count option works. + +Import("env") + +env.NanopbProto("fixed_count") +env.Object("fixed_count.pb.c") + +p = env.Program(["fixed_count_unittests.c", + "fixed_count.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_decode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(p) diff --git a/tests/fixed_count/fixed_count.proto b/tests/fixed_count/fixed_count.proto new file mode 100644 index 0000000..e96d1ec --- /dev/null +++ b/tests/fixed_count/fixed_count.proto @@ -0,0 +1,21 @@ +/* Test nanopb fixed count option. */ + +syntax = "proto2"; + +import "nanopb.proto"; + +message Message1 +{ + repeated int32 data = 1 [(nanopb).max_count = 3, (nanopb).fixed_count = true]; +} + +message Message2 +{ + repeated Message1 data = 1 [(nanopb).max_count = 2, (nanopb).fixed_count = true]; +} + +message Message3 +{ + repeated Message2 data1 = 1 [(nanopb).max_count = 2, (nanopb).fixed_count = true]; + repeated Message2 data2 = 2 [(nanopb).max_count = 2, (nanopb).fixed_count = true]; +} diff --git a/tests/fixed_count/fixed_count_unittests.c b/tests/fixed_count/fixed_count_unittests.c new file mode 100644 index 0000000..02bacac --- /dev/null +++ b/tests/fixed_count/fixed_count_unittests.c @@ -0,0 +1,116 @@ +#include <stdio.h> +#include <string.h> +#include <pb_decode.h> +#include <pb_encode.h> +#include "unittests.h" +#include "fixed_count.pb.h" + +int main() +{ + int status = 0; + COMMENT("Test encoding and decoding of repeated fixed count fields"); + + { + pb_byte_t buffer[Message1_size]; + Message1 msg_a = Message1_init_zero; + Message1 msg_b = Message1_init_zero; + + pb_ostream_t ostream; + pb_istream_t istream; + size_t message_length; + + msg_a.data[0] = 1; + msg_a.data[0] = 2; + msg_a.data[0] = 3; + + ostream = pb_ostream_from_buffer(buffer, Message1_size); + TEST(pb_encode(&ostream, Message1_fields, &msg_a)); + message_length = ostream.bytes_written; + + istream = pb_istream_from_buffer(buffer, message_length); + TEST(pb_decode(&istream, Message1_fields, &msg_b)); + + TEST(istream.bytes_left == 0); + TEST(memcmp(&msg_b, &msg_a, sizeof(msg_a)) == 0); + } + + { + pb_byte_t buffer[Message2_size]; + Message2 msg_a = Message2_init_zero; + Message2 msg_b = Message2_init_zero; + + pb_ostream_t ostream; + pb_istream_t istream; + size_t message_length; + + msg_a.data[0].data[0] = 1; + msg_a.data[0].data[1] = 2; + msg_a.data[0].data[2] = 3; + msg_a.data[1].data[0] = 4; + msg_a.data[1].data[1] = 5; + msg_a.data[1].data[2] = 6; + + ostream = pb_ostream_from_buffer(buffer, Message2_size); + TEST(pb_encode(&ostream, Message2_fields, &msg_a)); + message_length = ostream.bytes_written; + + istream = pb_istream_from_buffer(buffer, message_length); + TEST(pb_decode(&istream, Message2_fields, &msg_b)); + + TEST(istream.bytes_left == 0); + TEST(memcmp(&msg_b, &msg_a, sizeof(msg_a)) == 0); + } + + { + pb_byte_t buffer[Message3_size]; + Message3 msg_a = Message3_init_zero; + Message3 msg_b = Message3_init_zero; + + pb_ostream_t ostream; + pb_istream_t istream; + size_t message_length; + + msg_a.data1[0].data[0].data[0] = 1; + msg_a.data1[0].data[0].data[1] = 2; + msg_a.data1[0].data[0].data[2] = 3; + msg_a.data1[0].data[1].data[0] = 4; + msg_a.data1[0].data[1].data[1] = 5; + msg_a.data1[0].data[1].data[2] = 6; + + msg_a.data1[1].data[0].data[0] = 7; + msg_a.data1[1].data[0].data[1] = 8; + msg_a.data1[1].data[0].data[2] = 9; + msg_a.data1[1].data[1].data[0] = 10; + msg_a.data1[1].data[1].data[1] = 11; + msg_a.data1[1].data[1].data[2] = 12; + + msg_a.data2[0].data[0].data[0] = 11; + msg_a.data2[0].data[0].data[1] = 12; + msg_a.data2[0].data[0].data[2] = 13; + msg_a.data2[0].data[1].data[0] = 14; + msg_a.data2[0].data[1].data[1] = 15; + msg_a.data2[0].data[1].data[2] = 16; + + msg_a.data2[1].data[0].data[0] = 17; + msg_a.data2[1].data[0].data[1] = 18; + msg_a.data2[1].data[0].data[2] = 19; + msg_a.data2[1].data[1].data[0] = 110; + msg_a.data2[1].data[1].data[1] = 111; + msg_a.data2[1].data[1].data[2] = 112; + + ostream = pb_ostream_from_buffer(buffer, Message3_size); + TEST(pb_encode(&ostream, Message3_fields, &msg_a)); + message_length = ostream.bytes_written; + + istream = pb_istream_from_buffer(buffer, message_length); + TEST(pb_decode(&istream, Message3_fields, &msg_b)); + + TEST(istream.bytes_left == 0); + TEST(memcmp(&msg_b, &msg_a, sizeof(msg_a)) == 0); + } + + if (status != 0) + fprintf(stdout, "\n\nSome tests FAILED!\n"); + + return status; +} diff --git a/tests/fuzztest/SConscript b/tests/fuzztest/SConscript new file mode 100644 index 0000000..d2fb689 --- /dev/null +++ b/tests/fuzztest/SConscript @@ -0,0 +1,43 @@ +# Run a fuzz test to verify robustness against corrupted/malicious data. + +Import("env", "malloc_env") + +def set_pkgname(src, dst, pkgname): + data = open(str(src)).read() + placeholder = '// package name placeholder' + assert placeholder in data + data = data.replace(placeholder, 'package %s;' % pkgname) + open(str(dst), 'w').write(data) + +# We want both pointer and static versions of the AllTypes message +# Prefix them with package name. +env.Command("alltypes_static.proto", "#alltypes/alltypes.proto", + lambda target, source, env: set_pkgname(source[0], target[0], 'alltypes_static')) +env.Command("alltypes_pointer.proto", "#alltypes/alltypes.proto", + lambda target, source, env: set_pkgname(source[0], target[0], 'alltypes_pointer')) + +p1 = env.NanopbProto(["alltypes_pointer", "alltypes_pointer.options"]) +p2 = env.NanopbProto(["alltypes_static", "alltypes_static.options"]) +fuzz = malloc_env.Program(["fuzztest.c", + "alltypes_pointer.pb.c", + "alltypes_static.pb.c", + "$COMMON/pb_encode_with_malloc.o", + "$COMMON/pb_decode_with_malloc.o", + "$COMMON/pb_common_with_malloc.o", + "$COMMON/malloc_wrappers.o"]) + +env.RunTest(fuzz) + +fuzzstub = malloc_env.Program(["fuzzstub.c", + "alltypes_pointer.pb.c", + "alltypes_static.pb.c", + "$COMMON/pb_encode_with_malloc.o", + "$COMMON/pb_decode_with_malloc.o", + "$COMMON/pb_common_with_malloc.o", + "$COMMON/malloc_wrappers.o"]) + +generate_message = malloc_env.Program(["generate_message.c", + "alltypes_static.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) + diff --git a/tests/fuzztest/alltypes_pointer.options b/tests/fuzztest/alltypes_pointer.options new file mode 100644 index 0000000..7e3ad1e --- /dev/null +++ b/tests/fuzztest/alltypes_pointer.options @@ -0,0 +1,3 @@ +# Generate all fields as pointers. +* type:FT_POINTER +*.*fbytes fixed_length:true max_size:4 diff --git a/tests/fuzztest/alltypes_static.options b/tests/fuzztest/alltypes_static.options new file mode 100644 index 0000000..e197e1d --- /dev/null +++ b/tests/fuzztest/alltypes_static.options @@ -0,0 +1,4 @@ +* max_size:32 +* max_count:8 +*.extensions type:FT_IGNORE +*.*fbytes fixed_length:true max_size:4 diff --git a/tests/fuzztest/fuzzstub.c b/tests/fuzztest/fuzzstub.c new file mode 100644 index 0000000..ec9e2af --- /dev/null +++ b/tests/fuzztest/fuzzstub.c @@ -0,0 +1,189 @@ +/* Fuzz testing for the nanopb core. + * This can be used with external fuzzers, e.g. radamsa. + * It performs most of the same checks as fuzztest, but does not feature data generation. + */ + +#include <pb_decode.h> +#include <pb_encode.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <time.h> +#include <malloc_wrappers.h> +#include "alltypes_static.pb.h" +#include "alltypes_pointer.pb.h" + +#define BUFSIZE 4096 + +static bool do_static_decode(uint8_t *buffer, size_t msglen, bool assert_success) +{ + pb_istream_t stream; + bool status; + + alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes)); + stream = pb_istream_from_buffer(buffer, msglen); + status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg); + + if (!status && assert_success) + { + /* Anything that was successfully encoded, should be decodeable. + * One exception: strings without null terminator are encoded up + * to end of buffer, but refused on decode because the terminator + * would not fit. */ + if (strcmp(stream.errmsg, "string overflow") != 0) + assert(status); + } + + free_with_check(msg); + return status; +} + +static bool do_pointer_decode(uint8_t *buffer, size_t msglen, bool assert_success) +{ + pb_istream_t stream; + bool status; + alltypes_pointer_AllTypes *msg; + + msg = malloc_with_check(sizeof(alltypes_pointer_AllTypes)); + memset(msg, 0, sizeof(alltypes_pointer_AllTypes)); + stream = pb_istream_from_buffer(buffer, msglen); + + assert(get_alloc_count() == 0); + status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg); + + if (assert_success) + assert(status); + + pb_release(alltypes_pointer_AllTypes_fields, msg); + assert(get_alloc_count() == 0); + + free_with_check(msg); + + return status; +} + +/* Do a decode -> encode -> decode -> encode roundtrip */ +static void do_static_roundtrip(uint8_t *buffer, size_t msglen) +{ + bool status; + uint8_t *buf2 = malloc_with_check(BUFSIZE); + uint8_t *buf3 = malloc_with_check(BUFSIZE); + size_t msglen2, msglen3; + alltypes_static_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_static_AllTypes)); + alltypes_static_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_static_AllTypes)); + memset(msg1, 0, sizeof(alltypes_static_AllTypes)); + memset(msg2, 0, sizeof(alltypes_static_AllTypes)); + + { + pb_istream_t stream = pb_istream_from_buffer(buffer, msglen); + status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg1); + assert(status); + } + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE); + status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg1); + assert(status); + msglen2 = stream.bytes_written; + } + + { + pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2); + status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg2); + assert(status); + } + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE); + status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg2); + assert(status); + msglen3 = stream.bytes_written; + } + + assert(msglen2 == msglen3); + assert(memcmp(buf2, buf3, msglen2) == 0); + + free_with_check(msg1); + free_with_check(msg2); + free_with_check(buf2); + free_with_check(buf3); +} + +/* Do decode -> encode -> decode -> encode roundtrip */ +static void do_pointer_roundtrip(uint8_t *buffer, size_t msglen) +{ + bool status; + uint8_t *buf2 = malloc_with_check(BUFSIZE); + uint8_t *buf3 = malloc_with_check(BUFSIZE); + size_t msglen2, msglen3; + alltypes_pointer_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_pointer_AllTypes)); + alltypes_pointer_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_pointer_AllTypes)); + memset(msg1, 0, sizeof(alltypes_pointer_AllTypes)); + memset(msg2, 0, sizeof(alltypes_pointer_AllTypes)); + + { + pb_istream_t stream = pb_istream_from_buffer(buffer, msglen); + status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg1); + assert(status); + } + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE); + status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg1); + assert(status); + msglen2 = stream.bytes_written; + } + + { + pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2); + status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg2); + assert(status); + } + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE); + status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg2); + assert(status); + msglen3 = stream.bytes_written; + } + + assert(msglen2 == msglen3); + assert(memcmp(buf2, buf3, msglen2) == 0); + + pb_release(alltypes_pointer_AllTypes_fields, msg1); + pb_release(alltypes_pointer_AllTypes_fields, msg2); + free_with_check(msg1); + free_with_check(msg2); + free_with_check(buf2); + free_with_check(buf3); +} + +static void run_iteration() +{ + uint8_t *buffer = malloc_with_check(BUFSIZE); + size_t msglen; + bool status; + + msglen = fread(buffer, 1, BUFSIZE, stdin); + + status = do_static_decode(buffer, msglen, false); + + if (status) + do_static_roundtrip(buffer, msglen); + + status = do_pointer_decode(buffer, msglen, false); + + if (status) + do_pointer_roundtrip(buffer, msglen); + + free_with_check(buffer); +} + +int main(int argc, char **argv) +{ + run_iteration(); + + return 0; +} + diff --git a/tests/fuzztest/fuzztest.c b/tests/fuzztest/fuzztest.c new file mode 100644 index 0000000..ee851ec --- /dev/null +++ b/tests/fuzztest/fuzztest.c @@ -0,0 +1,432 @@ +/* Fuzz testing for the nanopb core. + * Attempts to verify all the properties defined in the security model document. + */ + +#include <pb_decode.h> +#include <pb_encode.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <time.h> +#include <malloc_wrappers.h> +#include "alltypes_static.pb.h" +#include "alltypes_pointer.pb.h" + +static uint64_t random_seed; + +/* Uses xorshift64 here instead of rand() for both speed and + * reproducibility across platforms. */ +static uint32_t rand_word() +{ + random_seed ^= random_seed >> 12; + random_seed ^= random_seed << 25; + random_seed ^= random_seed >> 27; + return random_seed * 2685821657736338717ULL; +} + +/* Get a random integer in range, with approximately flat distribution. */ +static int rand_int(int min, int max) +{ + return rand_word() % (max + 1 - min) + min; +} + +static bool rand_bool() +{ + return rand_word() & 1; +} + +/* Get a random byte, with skewed distribution. + * Important corner cases like 0xFF, 0x00 and 0xFE occur more + * often than other values. */ +static uint8_t rand_byte() +{ + uint32_t w = rand_word(); + uint8_t b = w & 0xFF; + if (w & 0x100000) + b >>= (w >> 8) & 7; + if (w & 0x200000) + b <<= (w >> 12) & 7; + if (w & 0x400000) + b ^= 0xFF; + return b; +} + +/* Get a random length, with skewed distribution. + * Favors the shorter lengths, but always atleast 1. */ +static size_t rand_len(size_t max) +{ + uint32_t w = rand_word(); + size_t s; + if (w & 0x800000) + w &= 3; + else if (w & 0x400000) + w &= 15; + else if (w & 0x200000) + w &= 255; + + s = (w % max); + if (s == 0) + s = 1; + + return s; +} + +/* Fills a buffer with random data with skewed distribution. */ +static void rand_fill(uint8_t *buf, size_t count) +{ + while (count--) + *buf++ = rand_byte(); +} + +/* Fill with random protobuf-like data */ +static size_t rand_fill_protobuf(uint8_t *buf, size_t min_bytes, size_t max_bytes, int min_tag) +{ + pb_ostream_t stream = pb_ostream_from_buffer(buf, max_bytes); + + while(stream.bytes_written < min_bytes) + { + pb_wire_type_t wt = rand_int(0, 3); + if (wt == 3) wt = 5; /* Gap in values */ + + if (!pb_encode_tag(&stream, wt, rand_int(min_tag, min_tag + 512))) + break; + + if (wt == PB_WT_VARINT) + { + uint64_t value; + rand_fill((uint8_t*)&value, sizeof(value)); + pb_encode_varint(&stream, value); + } + else if (wt == PB_WT_64BIT) + { + uint64_t value; + rand_fill((uint8_t*)&value, sizeof(value)); + pb_encode_fixed64(&stream, &value); + } + else if (wt == PB_WT_32BIT) + { + uint32_t value; + rand_fill((uint8_t*)&value, sizeof(value)); + pb_encode_fixed32(&stream, &value); + } + else if (wt == PB_WT_STRING) + { + size_t len; + uint8_t *buf; + + if (min_bytes > stream.bytes_written) + len = rand_len(min_bytes - stream.bytes_written); + else + len = 0; + + buf = malloc(len); + pb_encode_varint(&stream, len); + rand_fill(buf, len); + pb_write(&stream, buf, len); + free(buf); + } + } + + return stream.bytes_written; +} + +/* Given a buffer of data, mess it up a bit */ +static void rand_mess(uint8_t *buf, size_t count) +{ + int m = rand_int(0, 3); + + if (m == 0) + { + /* Replace random substring */ + int s = rand_int(0, count - 1); + int l = rand_len(count - s); + rand_fill(buf + s, l); + } + else if (m == 1) + { + /* Swap random bytes */ + int a = rand_int(0, count - 1); + int b = rand_int(0, count - 1); + int x = buf[a]; + buf[a] = buf[b]; + buf[b] = x; + } + else if (m == 2) + { + /* Duplicate substring */ + int s = rand_int(0, count - 2); + int l = rand_len((count - s) / 2); + memcpy(buf + s + l, buf + s, l); + } + else if (m == 3) + { + /* Add random protobuf noise */ + int s = rand_int(0, count - 1); + int l = rand_len(count - s); + rand_fill_protobuf(buf + s, l, count - s, 1); + } +} + +/* Some default data to put in the message */ +static const alltypes_static_AllTypes initval = alltypes_static_AllTypes_init_default; + +#define BUFSIZE 4096 + +static bool do_static_encode(uint8_t *buffer, size_t *msglen) +{ + pb_ostream_t stream; + bool status; + + /* Allocate a message and fill it with defaults */ + alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes)); + memcpy(msg, &initval, sizeof(initval)); + + /* Apply randomness to the data before encoding */ + while (rand_int(0, 7)) + rand_mess((uint8_t*)msg, sizeof(alltypes_static_AllTypes)); + + stream = pb_ostream_from_buffer(buffer, BUFSIZE); + status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg); + assert(stream.bytes_written <= BUFSIZE); + assert(stream.bytes_written <= alltypes_static_AllTypes_size); + + *msglen = stream.bytes_written; + pb_release(alltypes_static_AllTypes_fields, msg); + free_with_check(msg); + + return status; +} + +/* Append or prepend protobuf noise */ +static void do_protobuf_noise(uint8_t *buffer, size_t *msglen) +{ + int m = rand_int(0, 2); + size_t max_size = BUFSIZE - 32 - *msglen; + if (m == 1) + { + /* Prepend */ + uint8_t *tmp = malloc_with_check(BUFSIZE); + size_t s = rand_fill_protobuf(tmp, rand_len(max_size), BUFSIZE - *msglen, 512); + memmove(buffer + s, buffer, *msglen); + memcpy(buffer, tmp, s); + free_with_check(tmp); + *msglen += s; + } + else if (m == 2) + { + /* Append */ + size_t s = rand_fill_protobuf(buffer + *msglen, rand_len(max_size), BUFSIZE - *msglen, 512); + *msglen += s; + } +} + +static bool do_static_decode(uint8_t *buffer, size_t msglen, bool assert_success) +{ + pb_istream_t stream; + bool status; + + alltypes_static_AllTypes *msg = malloc_with_check(sizeof(alltypes_static_AllTypes)); + rand_fill((uint8_t*)msg, sizeof(alltypes_static_AllTypes)); + stream = pb_istream_from_buffer(buffer, msglen); + status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg); + + if (!status && assert_success) + { + /* Anything that was successfully encoded, should be decodeable. + * One exception: strings without null terminator are encoded up + * to end of buffer, but refused on decode because the terminator + * would not fit. */ + if (strcmp(stream.errmsg, "string overflow") != 0) + assert(status); + } + + free_with_check(msg); + return status; +} + +static bool do_pointer_decode(uint8_t *buffer, size_t msglen, bool assert_success) +{ + pb_istream_t stream; + bool status; + alltypes_pointer_AllTypes *msg; + + msg = malloc_with_check(sizeof(alltypes_pointer_AllTypes)); + memset(msg, 0, sizeof(alltypes_pointer_AllTypes)); + stream = pb_istream_from_buffer(buffer, msglen); + + assert(get_alloc_count() == 0); + status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg); + + if (assert_success) + assert(status); + + pb_release(alltypes_pointer_AllTypes_fields, msg); + assert(get_alloc_count() == 0); + + free_with_check(msg); + + return status; +} + +/* Do a decode -> encode -> decode -> encode roundtrip */ +static void do_static_roundtrip(uint8_t *buffer, size_t msglen) +{ + bool status; + uint8_t *buf2 = malloc_with_check(BUFSIZE); + uint8_t *buf3 = malloc_with_check(BUFSIZE); + size_t msglen2, msglen3; + alltypes_static_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_static_AllTypes)); + alltypes_static_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_static_AllTypes)); + memset(msg1, 0, sizeof(alltypes_static_AllTypes)); + memset(msg2, 0, sizeof(alltypes_static_AllTypes)); + + { + pb_istream_t stream = pb_istream_from_buffer(buffer, msglen); + status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg1); + assert(status); + } + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE); + status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg1); + assert(status); + msglen2 = stream.bytes_written; + } + + { + pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2); + status = pb_decode(&stream, alltypes_static_AllTypes_fields, msg2); + assert(status); + } + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE); + status = pb_encode(&stream, alltypes_static_AllTypes_fields, msg2); + assert(status); + msglen3 = stream.bytes_written; + } + + assert(msglen2 == msglen3); + assert(memcmp(buf2, buf3, msglen2) == 0); + + free_with_check(msg1); + free_with_check(msg2); + free_with_check(buf2); + free_with_check(buf3); +} + +/* Do decode -> encode -> decode -> encode roundtrip */ +static void do_pointer_roundtrip(uint8_t *buffer, size_t msglen) +{ + bool status; + uint8_t *buf2 = malloc_with_check(BUFSIZE); + uint8_t *buf3 = malloc_with_check(BUFSIZE); + size_t msglen2, msglen3; + alltypes_pointer_AllTypes *msg1 = malloc_with_check(sizeof(alltypes_pointer_AllTypes)); + alltypes_pointer_AllTypes *msg2 = malloc_with_check(sizeof(alltypes_pointer_AllTypes)); + memset(msg1, 0, sizeof(alltypes_pointer_AllTypes)); + memset(msg2, 0, sizeof(alltypes_pointer_AllTypes)); + + { + pb_istream_t stream = pb_istream_from_buffer(buffer, msglen); + status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg1); + assert(status); + } + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf2, BUFSIZE); + status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg1); + assert(status); + msglen2 = stream.bytes_written; + } + + { + pb_istream_t stream = pb_istream_from_buffer(buf2, msglen2); + status = pb_decode(&stream, alltypes_pointer_AllTypes_fields, msg2); + assert(status); + } + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf3, BUFSIZE); + status = pb_encode(&stream, alltypes_pointer_AllTypes_fields, msg2); + assert(status); + msglen3 = stream.bytes_written; + } + + assert(msglen2 == msglen3); + assert(memcmp(buf2, buf3, msglen2) == 0); + + pb_release(alltypes_pointer_AllTypes_fields, msg1); + pb_release(alltypes_pointer_AllTypes_fields, msg2); + free_with_check(msg1); + free_with_check(msg2); + free_with_check(buf2); + free_with_check(buf3); +} + +static void run_iteration() +{ + uint8_t *buffer = malloc_with_check(BUFSIZE); + size_t msglen; + bool status; + + rand_fill(buffer, BUFSIZE); + + if (do_static_encode(buffer, &msglen)) + { + do_protobuf_noise(buffer, &msglen); + + status = do_static_decode(buffer, msglen, true); + + if (status) + do_static_roundtrip(buffer, msglen); + + status = do_pointer_decode(buffer, msglen, true); + + if (status) + do_pointer_roundtrip(buffer, msglen); + + /* Apply randomness to the encoded data */ + while (rand_bool()) + rand_mess(buffer, BUFSIZE); + + /* Apply randomness to encoded data length */ + if (rand_bool()) + msglen = rand_int(0, BUFSIZE); + + status = do_static_decode(buffer, msglen, false); + do_pointer_decode(buffer, msglen, status); + + if (status) + { + do_static_roundtrip(buffer, msglen); + do_pointer_roundtrip(buffer, msglen); + } + } + + free_with_check(buffer); +} + +int main(int argc, char **argv) +{ + int i; + if (argc > 1) + { + random_seed = atol(argv[1]); + } + else + { + random_seed = time(NULL); + } + + fprintf(stderr, "Random seed: %llu\n", (long long unsigned)random_seed); + + for (i = 0; i < 10000; i++) + { + run_iteration(); + } + + return 0; +} + diff --git a/tests/fuzztest/generate_message.c b/tests/fuzztest/generate_message.c new file mode 100644 index 0000000..6e49299 --- /dev/null +++ b/tests/fuzztest/generate_message.c @@ -0,0 +1,101 @@ +/* Generates a random, valid protobuf message. Useful to seed + * external fuzzers such as afl-fuzz. + */ + +#include <pb_encode.h> +#include <pb_common.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <time.h> +#include "alltypes_static.pb.h" + +static uint64_t random_seed; + +/* Uses xorshift64 here instead of rand() for both speed and + * reproducibility across platforms. */ +static uint32_t rand_word() +{ + random_seed ^= random_seed >> 12; + random_seed ^= random_seed << 25; + random_seed ^= random_seed >> 27; + return random_seed * 2685821657736338717ULL; +} + +/* Fills a buffer with random data. */ +static void rand_fill(uint8_t *buf, size_t count) +{ + while (count--) + { + *buf++ = rand_word() & 0xff; + } +} + +/* Check that size/count fields do not exceed their max size. + * Otherwise we would have to loop pretty long in generate_message(). + * Note that there may still be a few encoding errors from submessages. + */ +static void limit_sizes(alltypes_static_AllTypes *msg) +{ + pb_field_iter_t iter; + pb_field_iter_begin(&iter, alltypes_static_AllTypes_fields, msg); + while (pb_field_iter_next(&iter)) + { + if (PB_LTYPE(iter.pos->type) == PB_LTYPE_BYTES) + { + ((pb_bytes_array_t*)iter.pData)->size %= iter.pos->data_size - PB_BYTES_ARRAY_T_ALLOCSIZE(0); + } + + if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REPEATED) + { + *((pb_size_t*)iter.pSize) %= iter.pos->array_size; + } + + if (PB_HTYPE(iter.pos->type) == PB_HTYPE_ONEOF) + { + /* Set the oneof to this message type with 50% chance. */ + if (rand_word() & 1) + { + *((pb_size_t*)iter.pSize) = iter.pos->tag; + } + } + } +} + +static void generate_message() +{ + alltypes_static_AllTypes msg; + uint8_t buf[8192]; + pb_ostream_t stream = {0}; + + do { + if (stream.errmsg) + fprintf(stderr, "Encoder error: %s\n", stream.errmsg); + + stream = pb_ostream_from_buffer(buf, sizeof(buf)); + rand_fill((void*)&msg, sizeof(msg)); + limit_sizes(&msg); + } while (!pb_encode(&stream, alltypes_static_AllTypes_fields, &msg)); + + fwrite(buf, 1, stream.bytes_written, stdout); +} + +int main(int argc, char **argv) +{ + if (argc > 1) + { + random_seed = atol(argv[1]); + } + else + { + random_seed = time(NULL); + } + + fprintf(stderr, "Random seed: %llu\n", (long long unsigned)random_seed); + + generate_message(); + + return 0; +} + diff --git a/tests/fuzztest/run_radamsa.sh b/tests/fuzztest/run_radamsa.sh new file mode 100755 index 0000000..52cd40a --- /dev/null +++ b/tests/fuzztest/run_radamsa.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +TMP=`tempfile` + +echo $TMP +while true +do + radamsa sample_data/* > $TMP + $1 < $TMP + test $? -gt 127 && break +done + diff --git a/tests/fuzztest/sample_data/sample1.pb b/tests/fuzztest/sample_data/sample1.pb Binary files differnew file mode 100644 index 0000000..0752788 --- /dev/null +++ b/tests/fuzztest/sample_data/sample1.pb diff --git a/tests/fuzztest/sample_data/sample2.pb b/tests/fuzztest/sample_data/sample2.pb Binary files differnew file mode 100644 index 0000000..cc89f91 --- /dev/null +++ b/tests/fuzztest/sample_data/sample2.pb diff --git a/tests/inline/SConscript b/tests/inline/SConscript new file mode 100644 index 0000000..34371fd --- /dev/null +++ b/tests/inline/SConscript @@ -0,0 +1,16 @@ +# Test that inlined bytes fields work. + +Import("env") + +env.NanopbProto("inline") +env.Object("inline.pb.c") + +env.Match(["inline.pb.h", "inline.expected"]) + +p = env.Program(["inline_unittests.c", + "inline.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_decode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(p) diff --git a/tests/inline/inline.expected b/tests/inline/inline.expected new file mode 100644 index 0000000..593e972 --- /dev/null +++ b/tests/inline/inline.expected @@ -0,0 +1,3 @@ +pb_byte_t data\[32\]; +bool has_data; +pb_byte_t data\[64\]; diff --git a/tests/inline/inline.proto b/tests/inline/inline.proto new file mode 100644 index 0000000..6e511f0 --- /dev/null +++ b/tests/inline/inline.proto @@ -0,0 +1,17 @@ +/* Test nanopb option parsing. + * options.expected lists the patterns that are searched for in the output. + */ + +syntax = "proto2"; + +import "nanopb.proto"; + +message Message1 +{ + required bytes data = 1 [(nanopb).type = FT_INLINE, (nanopb).max_size = 32]; +} + +message Message2 +{ + optional bytes data = 1 [(nanopb).type = FT_INLINE, (nanopb).max_size = 64]; +} diff --git a/tests/inline/inline_unittests.c b/tests/inline/inline_unittests.c new file mode 100644 index 0000000..b5834c7 --- /dev/null +++ b/tests/inline/inline_unittests.c @@ -0,0 +1,73 @@ +#include <stdio.h> +#include <string.h> +#include <pb_decode.h> +#include <pb_encode.h> +#include "unittests.h" +#include "inline.pb.h" + +int main() +{ + int status = 0; + int i = 0; + COMMENT("Test inline byte fields"); + + { + Message1 msg1 = Message1_init_zero; + TEST(sizeof(msg1.data) == 32); + } + + { + Message1 msg1 = Message1_init_zero; + pb_byte_t msg1_buffer[Message1_size]; + pb_ostream_t ostream = pb_ostream_from_buffer(msg1_buffer, Message1_size); + Message1 msg1_deserialized = Message1_init_zero; + pb_istream_t istream = pb_istream_from_buffer(msg1_buffer, Message1_size); + + for (i = 0; i < 32; i++) { + msg1.data[i] = i; + } + + TEST(pb_encode(&ostream, Message1_fields, &msg1)); + TEST(ostream.bytes_written == Message1_size); + + TEST(pb_decode(&istream, Message1_fields, &msg1_deserialized)); + + TEST(istream.bytes_left == 0); + TEST(memcmp(&msg1_deserialized, &msg1, sizeof(msg1)) == 0); + } + + { + Message2 msg2 = {true, {0}}; + Message2 msg2_no_data = {false, {1}}; + pb_byte_t msg2_buffer[Message2_size]; + pb_ostream_t ostream = pb_ostream_from_buffer(msg2_buffer, Message2_size); + Message2 msg2_deserialized = Message2_init_zero; + pb_istream_t istream = pb_istream_from_buffer(msg2_buffer, Message2_size); + + for (i = 0; i < 64; i++) { + msg2.data[i] = i; + } + + TEST(pb_encode(&ostream, Message2_fields, &msg2)); + TEST(ostream.bytes_written == Message2_size); + + TEST(pb_decode(&istream, Message2_fields, &msg2_deserialized)); + + TEST(istream.bytes_left == 0); + TEST(memcmp(&msg2_deserialized, &msg2, sizeof(msg2)) == 0); + TEST(msg2_deserialized.has_data); + + memset(msg2_buffer, 0, sizeof(msg2_buffer)); + ostream = pb_ostream_from_buffer(msg2_buffer, Message2_size); + TEST(pb_encode(&ostream, Message2_fields, &msg2_no_data)); + istream = pb_istream_from_buffer(msg2_buffer, Message2_size); + TEST(pb_decode(&istream, Message2_fields, &msg2_deserialized)); + TEST(!msg2_deserialized.has_data); + TEST(memcmp(&msg2_deserialized, &msg2, sizeof(msg2)) != 0); + } + + if (status != 0) + fprintf(stdout, "\n\nSome tests FAILED!\n"); + + return status; +} diff --git a/tests/intsizes/SConscript b/tests/intsizes/SConscript new file mode 100644 index 0000000..a90680b --- /dev/null +++ b/tests/intsizes/SConscript @@ -0,0 +1,12 @@ +# Test that the int_size option in .proto works. + +Import('env') + +env.NanopbProto('intsizes') + +p = env.Program(["intsizes_unittests.c", + "intsizes.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_decode.o", + "$COMMON/pb_common.o"]) +env.RunTest(p) diff --git a/tests/intsizes/intsizes.proto b/tests/intsizes/intsizes.proto new file mode 100644 index 0000000..91444d4 --- /dev/null +++ b/tests/intsizes/intsizes.proto @@ -0,0 +1,41 @@ +/* Test the integer size overriding in nanopb options. + * This allows to use 8- and 16-bit integer variables, which are not supported + * directly by Google Protobuf. + * + * The int_size setting will override the number of bits, but keep the type + * otherwise. E.g. uint32 + IS_8 => uint8_t + */ + +syntax = "proto2"; + +import 'nanopb.proto'; + +message IntSizes { + required int32 req_int8 = 1 [(nanopb).int_size = IS_8]; + required uint32 req_uint8 = 2 [(nanopb).int_size = IS_8]; + required sint32 req_sint8 = 3 [(nanopb).int_size = IS_8]; + required int32 req_int16 = 4 [(nanopb).int_size = IS_16]; + required uint32 req_uint16 = 5 [(nanopb).int_size = IS_16]; + required sint32 req_sint16 = 6 [(nanopb).int_size = IS_16]; + required int32 req_int32 = 7 [(nanopb).int_size = IS_32]; + required uint32 req_uint32 = 8 [(nanopb).int_size = IS_32]; + required sint32 req_sint32 = 9 [(nanopb).int_size = IS_32]; + required int32 req_int64 = 10 [(nanopb).int_size = IS_64]; + required uint32 req_uint64 = 11 [(nanopb).int_size = IS_64]; + required sint32 req_sint64 = 12 [(nanopb).int_size = IS_64]; +} + +message DefaultSizes { + required int32 req_int8 = 1 ; + required uint32 req_uint8 = 2 ; + required sint32 req_sint8 = 3 ; + required int32 req_int16 = 4 ; + required uint32 req_uint16 = 5 ; + required sint32 req_sint16 = 6 ; + required int32 req_int32 = 7 ; + required uint32 req_uint32 = 8 ; + required sint32 req_sint32 = 9 ; + required int64 req_int64 = 10; + required uint64 req_uint64 = 11; + required sint64 req_sint64 = 12; +} diff --git a/tests/intsizes/intsizes_unittests.c b/tests/intsizes/intsizes_unittests.c new file mode 100644 index 0000000..79ef036 --- /dev/null +++ b/tests/intsizes/intsizes_unittests.c @@ -0,0 +1,122 @@ +#include <stdio.h> +#include <string.h> +#include <pb_decode.h> +#include <pb_encode.h> +#include "unittests.h" +#include "intsizes.pb.h" + +#define S(x) pb_istream_from_buffer((uint8_t*)x, sizeof(x) - 1) + +/* This is a macro instead of function in order to get the actual values + * into the TEST() lines in output */ +#define TEST_ROUNDTRIP(int8, uint8, sint8, \ + int16, uint16, sint16, \ + int32, uint32, sint32, \ + int64, uint64, sint64, expected_result) \ +{ \ + uint8_t buffer1[128], buffer2[128]; \ + size_t msgsize; \ + DefaultSizes msg1 = DefaultSizes_init_zero; \ + IntSizes msg2 = IntSizes_init_zero; \ + \ + msg1.req_int8 = int8; \ + msg1.req_uint8 = uint8; \ + msg1.req_sint8 = sint8; \ + msg1.req_int16 = int16; \ + msg1.req_uint16 = uint16; \ + msg1.req_sint16 = sint16; \ + msg1.req_int32 = int32; \ + msg1.req_uint32 = uint32; \ + msg1.req_sint32 = sint32; \ + msg1.req_int64 = int64; \ + msg1.req_uint64 = uint64; \ + msg1.req_sint64 = sint64; \ + \ + { \ + pb_ostream_t s = pb_ostream_from_buffer(buffer1, sizeof(buffer1)); \ + TEST(pb_encode(&s, DefaultSizes_fields, &msg1)); \ + msgsize = s.bytes_written; \ + } \ + \ + { \ + pb_istream_t s = pb_istream_from_buffer(buffer1, msgsize); \ + TEST(pb_decode(&s, IntSizes_fields, &msg2) == expected_result); \ + if (expected_result) \ + { \ + TEST( (int64_t)msg2.req_int8 == int8); \ + TEST((uint64_t)msg2.req_uint8 == uint8); \ + TEST( (int64_t)msg2.req_sint8 == sint8); \ + TEST( (int64_t)msg2.req_int16 == int16); \ + TEST((uint64_t)msg2.req_uint16 == uint16); \ + TEST( (int64_t)msg2.req_sint16 == sint16); \ + TEST( (int64_t)msg2.req_int32 == int32); \ + TEST((uint64_t)msg2.req_uint32 == uint32); \ + TEST( (int64_t)msg2.req_sint32 == sint32); \ + TEST( (int64_t)msg2.req_int64 == int64); \ + TEST((uint64_t)msg2.req_uint64 == uint64); \ + TEST( (int64_t)msg2.req_sint64 == sint64); \ + } \ + } \ + \ + if (expected_result) \ + { \ + pb_ostream_t s = pb_ostream_from_buffer(buffer2, sizeof(buffer2)); \ + TEST(pb_encode(&s, IntSizes_fields, &msg2)); \ + TEST(s.bytes_written == msgsize); \ + TEST(memcmp(buffer1, buffer2, msgsize) == 0); \ + } \ +} + +int main() +{ + int status = 0; + + { + IntSizes msg = IntSizes_init_zero; + + COMMENT("Test field sizes"); + TEST(sizeof(msg.req_int8) == 1); + TEST(sizeof(msg.req_uint8) == 1); + TEST(sizeof(msg.req_sint8) == 1); + TEST(sizeof(msg.req_int16) == 2); + TEST(sizeof(msg.req_uint16) == 2); + TEST(sizeof(msg.req_sint16) == 2); + TEST(sizeof(msg.req_int32) == 4); + TEST(sizeof(msg.req_uint32) == 4); + TEST(sizeof(msg.req_sint32) == 4); + TEST(sizeof(msg.req_int64) == 8); + TEST(sizeof(msg.req_uint64) == 8); + TEST(sizeof(msg.req_sint64) == 8); + } + + COMMENT("Test roundtrip at maximum value"); + TEST_ROUNDTRIP(127, 255, 127, + 32767, 65535, 32767, + INT32_MAX, UINT32_MAX, INT32_MAX, + INT64_MAX, UINT64_MAX, INT64_MAX, true); + + COMMENT("Test roundtrip at minimum value"); + TEST_ROUNDTRIP(-128, 0, -128, + -32768, 0, -32768, + INT32_MIN, 0, INT32_MIN, + INT64_MIN, 0, INT64_MIN, true); + + COMMENT("Test overflow detection"); + TEST_ROUNDTRIP(-129, 0, -128, + -32768, 0, -32768, + INT32_MIN, 0, INT32_MIN, + INT64_MIN, 0, INT64_MIN, false); + TEST_ROUNDTRIP(127, 256, 127, + 32767, 65535, 32767, + INT32_MAX, UINT32_MAX, INT32_MAX, + INT64_MAX, UINT64_MAX, INT64_MAX, false); + TEST_ROUNDTRIP(-128, 0, -128, + -32768, 0, -32769, + INT32_MIN, 0, INT32_MIN, + INT64_MIN, 0, INT64_MIN, false); + + if (status != 0) + fprintf(stdout, "\n\nSome tests FAILED!\n"); + + return status; +}
\ No newline at end of file diff --git a/tests/io_errors/SConscript b/tests/io_errors/SConscript new file mode 100644 index 0000000..60146cc --- /dev/null +++ b/tests/io_errors/SConscript @@ -0,0 +1,15 @@ +# Simulate io errors when encoding and decoding + +Import("env") + +c = Copy("$TARGET", "$SOURCE") +env.Command("alltypes.proto", "#alltypes/alltypes.proto", c) + +env.NanopbProto(["alltypes", "alltypes.options"]) + +ioerr = env.Program(["io_errors.c", "alltypes.pb.c", + "$COMMON/pb_encode.o", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) + +env.RunTest("io_errors.output", [ioerr, "$BUILD/alltypes/encode_alltypes.output"]) + + diff --git a/tests/io_errors/alltypes.options b/tests/io_errors/alltypes.options new file mode 100644 index 0000000..0d5ab12 --- /dev/null +++ b/tests/io_errors/alltypes.options @@ -0,0 +1,3 @@ +* max_size:16 +* max_count:5 +*.*fbytes fixed_length:true max_size:4 diff --git a/tests/io_errors/io_errors.c b/tests/io_errors/io_errors.c new file mode 100644 index 0000000..76f35b0 --- /dev/null +++ b/tests/io_errors/io_errors.c @@ -0,0 +1,140 @@ +/* Simulate IO errors after each byte in a stream. + * Verifies proper error propagation. + */ + +#include <stdio.h> +#include <pb_decode.h> +#include <pb_encode.h> +#include "alltypes.pb.h" +#include "test_helpers.h" + +typedef struct +{ + uint8_t *buffer; + size_t fail_after; +} faulty_stream_t; + +bool read_callback(pb_istream_t *stream, uint8_t *buf, size_t count) +{ + faulty_stream_t *state = stream->state; + + while (count--) + { + if (state->fail_after == 0) + PB_RETURN_ERROR(stream, "simulated"); + state->fail_after--; + *buf++ = *state->buffer++; + } + + return true; +} +bool write_callback(pb_ostream_t *stream, const uint8_t *buf, size_t count) +{ + faulty_stream_t *state = stream->state; + + while (count--) + { + if (state->fail_after == 0) + PB_RETURN_ERROR(stream, "simulated"); + state->fail_after--; + *state->buffer++ = *buf++; + } + + return true; +} + +int main() +{ + uint8_t buffer[2048]; + size_t msglen; + AllTypes msg = AllTypes_init_zero; + + /* Get some base data to run the tests with */ + SET_BINARY_MODE(stdin); + msglen = fread(buffer, 1, sizeof(buffer), stdin); + + /* Test IO errors on decoding */ + { + bool status; + pb_istream_t stream = {&read_callback, NULL, SIZE_MAX}; + faulty_stream_t fs; + size_t i; + + for (i = 0; i < msglen; i++) + { + stream.bytes_left = msglen; + stream.state = &fs; + fs.buffer = buffer; + fs.fail_after = i; + + status = pb_decode(&stream, AllTypes_fields, &msg); + if (status != false) + { + fprintf(stderr, "Unexpected success in decode\n"); + return 2; + } + else if (strcmp(stream.errmsg, "simulated") != 0) + { + fprintf(stderr, "Wrong error in decode: %s\n", stream.errmsg); + return 3; + } + } + + stream.bytes_left = msglen; + stream.state = &fs; + fs.buffer = buffer; + fs.fail_after = msglen; + status = pb_decode(&stream, AllTypes_fields, &msg); + + if (!status) + { + fprintf(stderr, "Decoding failed: %s\n", stream.errmsg); + return 4; + } + } + + /* Test IO errors on encoding */ + { + bool status; + pb_ostream_t stream = {&write_callback, NULL, SIZE_MAX, 0}; + faulty_stream_t fs; + size_t i; + + for (i = 0; i < msglen; i++) + { + stream.max_size = msglen; + stream.bytes_written = 0; + stream.state = &fs; + fs.buffer = buffer; + fs.fail_after = i; + + status = pb_encode(&stream, AllTypes_fields, &msg); + if (status != false) + { + fprintf(stderr, "Unexpected success in encode\n"); + return 5; + } + else if (strcmp(stream.errmsg, "simulated") != 0) + { + fprintf(stderr, "Wrong error in encode: %s\n", stream.errmsg); + return 6; + } + } + + stream.max_size = msglen; + stream.bytes_written = 0; + stream.state = &fs; + fs.buffer = buffer; + fs.fail_after = msglen; + status = pb_encode(&stream, AllTypes_fields, &msg); + + if (!status) + { + fprintf(stderr, "Encoding failed: %s\n", stream.errmsg); + return 7; + } + } + + return 0; +} + diff --git a/tests/io_errors_pointers/SConscript b/tests/io_errors_pointers/SConscript new file mode 100644 index 0000000..03727df --- /dev/null +++ b/tests/io_errors_pointers/SConscript @@ -0,0 +1,26 @@ +# Simulate io errors when encoding and decoding + +Import("env", "malloc_env") + +c = Copy("$TARGET", "$SOURCE") +env.Command("alltypes.proto", "#alltypes/alltypes.proto", c) +env.Command("io_errors.c", "#io_errors/io_errors.c", c) + +env.NanopbProto(["alltypes", "alltypes.options"]) + +ioerr = env.Program(["io_errors.c", "alltypes.pb.c", + "$COMMON/pb_encode_with_malloc.o", + "$COMMON/pb_decode_with_malloc.o", + "$COMMON/pb_common_with_malloc.o", + "$COMMON/malloc_wrappers.o"]) + +# Run tests under valgrind if available +valgrind = env.WhereIs('valgrind') +kwargs = {} +if valgrind: + kwargs['COMMAND'] = valgrind + kwargs['ARGS'] = ["-q", "--error-exitcode=99", ioerr[0].abspath] + +env.RunTest("io_errors.output", [ioerr, "$BUILD/alltypes/encode_alltypes.output"], **kwargs) + + diff --git a/tests/io_errors_pointers/alltypes.options b/tests/io_errors_pointers/alltypes.options new file mode 100644 index 0000000..7e3ad1e --- /dev/null +++ b/tests/io_errors_pointers/alltypes.options @@ -0,0 +1,3 @@ +# Generate all fields as pointers. +* type:FT_POINTER +*.*fbytes fixed_length:true max_size:4 diff --git a/tests/map/SConscript b/tests/map/SConscript new file mode 100644 index 0000000..f23e8ee --- /dev/null +++ b/tests/map/SConscript @@ -0,0 +1,21 @@ +# Example / test for handling 'map' type using the backwards compatibility +# in protobuf specification: +# https://developers.google.com/protocol-buffers/docs/proto3#maps + +Import('env') + +env.NanopbProto(['map', 'map.options']) + +enc = env.Program(['encode_map.c', + 'map.pb.c', + '$COMMON/pb_encode.o', + '$COMMON/pb_common.o']) + +dec = env.Program(['decode_map.c', + 'map.pb.c', + '$COMMON/pb_decode.o', + '$COMMON/pb_common.o']) + +env.RunTest("message.pb", enc) +env.RunTest("message.txt", [dec, 'message.pb']) + diff --git a/tests/map/decode_map.c b/tests/map/decode_map.c new file mode 100644 index 0000000..c798b03 --- /dev/null +++ b/tests/map/decode_map.c @@ -0,0 +1,60 @@ +/* Decode a message using map field */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pb_decode.h> +#include "map.pb.h" +#include "test_helpers.h" +#include "unittests.h" + +/* Helper function to find an entry in the list. Not as efficient as a real + * hashmap or similar would be, but suitable for small arrays. */ +MyMessage_NumbersEntry *find_entry(MyMessage *msg, const char *key) +{ + int i; + for (i = 0; i < msg->numbers_count; i++) + { + if (strcmp(msg->numbers[i].key, key) == 0) + { + return &msg->numbers[i]; + } + } + return NULL; +} + +int main(int argc, char **argv) +{ + uint8_t buffer[MyMessage_size]; + size_t count; + + SET_BINARY_MODE(stdin); + count = fread(buffer, 1, sizeof(buffer), stdin); + + if (!feof(stdin)) + { + printf("Message does not fit in buffer\n"); + return 1; + } + + { + int status = 0; + MyMessage msg = MyMessage_init_zero; + MyMessage_NumbersEntry *e; + pb_istream_t stream = pb_istream_from_buffer(buffer, count); + + if (!pb_decode(&stream, MyMessage_fields, &msg)) + { + fprintf(stderr, "Decoding failed\n"); + return 2; + } + + TEST((e = find_entry(&msg, "one")) && e->value == 1); + TEST((e = find_entry(&msg, "two")) && e->value == 2); + TEST((e = find_entry(&msg, "seven")) && e->value == 7); + TEST(!find_entry(&msg, "zero")); + + return status; + } +} + diff --git a/tests/map/encode_map.c b/tests/map/encode_map.c new file mode 100644 index 0000000..bd4ec12 --- /dev/null +++ b/tests/map/encode_map.c @@ -0,0 +1,37 @@ +/* Encode a message using map field */ + +#include <stdio.h> +#include <stdlib.h> +#include <pb_encode.h> +#include "map.pb.h" +#include "test_helpers.h" + +int main(int argc, char **argv) +{ + uint8_t buffer[MyMessage_size]; + MyMessage msg = MyMessage_init_zero; + pb_ostream_t stream; + + /* Fill in the map entries */ + msg.numbers_count = 3; + strncpy(msg.numbers[0].key, "one", sizeof(msg.numbers[0].key)); + strncpy(msg.numbers[1].key, "two", sizeof(msg.numbers[1].key)); + strncpy(msg.numbers[2].key, "seven", sizeof(msg.numbers[2].key)); + msg.numbers[0].value = 1; + msg.numbers[1].value = 2; + msg.numbers[2].value = 7; + + stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + + if (pb_encode(&stream, MyMessage_fields, &msg)) + { + SET_BINARY_MODE(stdout); + fwrite(buffer, 1, stream.bytes_written, stdout); + return 0; + } + else + { + fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream)); + return 1; + } +} diff --git a/tests/map/map.options b/tests/map/map.options new file mode 100644 index 0000000..1d7ebb6 --- /dev/null +++ b/tests/map/map.options @@ -0,0 +1,2 @@ +MyMessage.numbers max_count:10 +MyMessage.NumbersEntry.key max_size:16 diff --git a/tests/map/map.proto b/tests/map/map.proto new file mode 100644 index 0000000..f503625 --- /dev/null +++ b/tests/map/map.proto @@ -0,0 +1,6 @@ +syntax = "proto3"; + +message MyMessage { + map<string,uint32> numbers = 1; +} + diff --git a/tests/mem_release/SConscript b/tests/mem_release/SConscript new file mode 100644 index 0000000..6754e28 --- /dev/null +++ b/tests/mem_release/SConscript @@ -0,0 +1,13 @@ +Import("env", "malloc_env") + +env.NanopbProto("mem_release.proto") + +test = malloc_env.Program(["mem_release.c", + "mem_release.pb.c", + "$COMMON/pb_encode_with_malloc.o", + "$COMMON/pb_decode_with_malloc.o", + "$COMMON/pb_common_with_malloc.o", + "$COMMON/malloc_wrappers.o"]) + +env.RunTest(test) + diff --git a/tests/mem_release/mem_release.c b/tests/mem_release/mem_release.c new file mode 100644 index 0000000..dc6f87d --- /dev/null +++ b/tests/mem_release/mem_release.c @@ -0,0 +1,187 @@ +/* Make sure that all fields are freed in various scenarios. */ + +#include <pb_decode.h> +#include <pb_encode.h> +#include <malloc_wrappers.h> +#include <stdio.h> +#include <test_helpers.h> +#include "mem_release.pb.h" + +#define TEST(x) if (!(x)) { \ + fprintf(stderr, "Test " #x " on line %d failed.\n", __LINE__); \ + return false; \ + } + +static char *test_str_arr[] = {"1", "2", ""}; +static SubMessage test_msg_arr[] = {SubMessage_init_zero, SubMessage_init_zero}; +static pb_extension_t ext1, ext2; + +static void fill_TestMessage(TestMessage *msg) +{ + msg->static_req_submsg.dynamic_str = "12345"; + msg->static_req_submsg.dynamic_str_arr_count = 3; + msg->static_req_submsg.dynamic_str_arr = test_str_arr; + msg->static_req_submsg.dynamic_submsg_count = 2; + msg->static_req_submsg.dynamic_submsg = test_msg_arr; + msg->static_req_submsg.dynamic_submsg[1].dynamic_str = "abc"; + msg->static_opt_submsg.dynamic_str = "abc"; + msg->static_rep_submsg_count = 2; + msg->static_rep_submsg[1].dynamic_str = "abc"; + msg->has_static_opt_submsg = true; + msg->dynamic_submsg = &msg->static_req_submsg; + + msg->extensions = &ext1; + ext1.type = &dynamic_ext; + ext1.dest = &msg->static_req_submsg; + ext1.next = &ext2; + ext2.type = &static_ext; + ext2.dest = &msg->static_req_submsg; + ext2.next = NULL; +} + +/* Basic fields, nested submessages, extensions */ +static bool test_TestMessage() +{ + uint8_t buffer[256]; + size_t msgsize; + + /* Construct a message with various fields filled in */ + { + TestMessage msg = TestMessage_init_zero; + pb_ostream_t stream; + + fill_TestMessage(&msg); + + stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + if (!pb_encode(&stream, TestMessage_fields, &msg)) + { + fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream)); + return false; + } + msgsize = stream.bytes_written; + } + + /* Output encoded message for debug */ + SET_BINARY_MODE(stdout); + fwrite(buffer, 1, msgsize, stdout); + + /* Decode memory using dynamic allocation */ + { + TestMessage msg = TestMessage_init_zero; + pb_istream_t stream; + SubMessage ext2_dest; + + msg.extensions = &ext1; + ext1.type = &dynamic_ext; + ext1.dest = NULL; + ext1.next = &ext2; + ext2.type = &static_ext; + ext2.dest = &ext2_dest; + ext2.next = NULL; + + stream = pb_istream_from_buffer(buffer, msgsize); + if (!pb_decode(&stream, TestMessage_fields, &msg)) + { + fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&stream)); + return false; + } + + /* Make sure it encodes back to same data */ + { + uint8_t buffer2[256]; + pb_ostream_t ostream = pb_ostream_from_buffer(buffer2, sizeof(buffer2)); + TEST(pb_encode(&ostream, TestMessage_fields, &msg)); + TEST(ostream.bytes_written == msgsize); + TEST(memcmp(buffer, buffer2, msgsize) == 0); + } + + /* Make sure that malloc counters work */ + TEST(get_alloc_count() > 0); + + /* Make sure that pb_release releases everything */ + pb_release(TestMessage_fields, &msg); + TEST(get_alloc_count() == 0); + + /* Check that double-free is a no-op */ + pb_release(TestMessage_fields, &msg); + TEST(get_alloc_count() == 0); + } + + return true; +} + +/* Oneofs */ +static bool test_OneofMessage() +{ + uint8_t buffer[256]; + size_t msgsize; + + { + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + + /* Encode first with TestMessage */ + { + OneofMessage msg = OneofMessage_init_zero; + msg.which_msgs = OneofMessage_msg1_tag; + + fill_TestMessage(&msg.msgs.msg1); + + if (!pb_encode(&stream, OneofMessage_fields, &msg)) + { + fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream)); + return false; + } + } + + /* Encode second with SubMessage, invoking 'merge' behaviour */ + { + OneofMessage msg = OneofMessage_init_zero; + msg.which_msgs = OneofMessage_msg2_tag; + + msg.first = 999; + msg.msgs.msg2.dynamic_str = "ABCD"; + msg.last = 888; + + if (!pb_encode(&stream, OneofMessage_fields, &msg)) + { + fprintf(stderr, "Encode failed: %s\n", PB_GET_ERROR(&stream)); + return false; + } + } + msgsize = stream.bytes_written; + } + + { + OneofMessage msg = OneofMessage_init_zero; + pb_istream_t stream = pb_istream_from_buffer(buffer, msgsize); + if (!pb_decode(&stream, OneofMessage_fields, &msg)) + { + fprintf(stderr, "Decode failed: %s\n", PB_GET_ERROR(&stream)); + return false; + } + + TEST(msg.first == 999); + TEST(msg.which_msgs == OneofMessage_msg2_tag); + TEST(msg.msgs.msg2.dynamic_str); + TEST(strcmp(msg.msgs.msg2.dynamic_str, "ABCD") == 0); + TEST(msg.msgs.msg2.dynamic_str_arr == NULL); + TEST(msg.msgs.msg2.dynamic_submsg == NULL); + TEST(msg.last == 888); + + pb_release(OneofMessage_fields, &msg); + TEST(get_alloc_count() == 0); + pb_release(OneofMessage_fields, &msg); + TEST(get_alloc_count() == 0); + } + + return true; +} + +int main() +{ + if (test_TestMessage() && test_OneofMessage()) + return 0; + else + return 1; +} + diff --git a/tests/mem_release/mem_release.proto b/tests/mem_release/mem_release.proto new file mode 100644 index 0000000..0816dc2 --- /dev/null +++ b/tests/mem_release/mem_release.proto @@ -0,0 +1,35 @@ +syntax = "proto2"; +import "nanopb.proto"; + +message SubMessage +{ + optional string dynamic_str = 1 [(nanopb).type = FT_POINTER]; + repeated string dynamic_str_arr = 2 [(nanopb).type = FT_POINTER]; + repeated SubMessage dynamic_submsg = 3 [(nanopb).type = FT_POINTER]; +} + +message TestMessage +{ + required SubMessage static_req_submsg = 1 [(nanopb).type = FT_STATIC]; + optional SubMessage dynamic_submsg = 2 [(nanopb).type = FT_POINTER]; + optional SubMessage static_opt_submsg = 3 [(nanopb).type = FT_STATIC]; + repeated SubMessage static_rep_submsg = 4 [(nanopb).type = FT_STATIC, (nanopb).max_count=2]; + extensions 100 to 200; +} + +extend TestMessage +{ + optional SubMessage dynamic_ext = 100 [(nanopb).type = FT_POINTER]; + optional SubMessage static_ext = 101 [(nanopb).type = FT_STATIC]; +} + +message OneofMessage +{ + required int32 first = 1; + oneof msgs + { + TestMessage msg1 = 2; + SubMessage msg2 = 3; + } + required int32 last = 4; +} diff --git a/tests/message_sizes/messages1.proto b/tests/message_sizes/messages1.proto index 48af55a..b66fad7 100644 --- a/tests/message_sizes/messages1.proto +++ b/tests/message_sizes/messages1.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + enum MessageStatus { FAIL = 0; OK = 1; diff --git a/tests/message_sizes/messages2.proto b/tests/message_sizes/messages2.proto index 19fc11e..6761408 100644 --- a/tests/message_sizes/messages2.proto +++ b/tests/message_sizes/messages2.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + import 'nanopb.proto'; import 'messages1.proto'; diff --git a/tests/missing_fields/SConscript b/tests/missing_fields/SConscript index 9926efa..86ba083 100644 --- a/tests/missing_fields/SConscript +++ b/tests/missing_fields/SConscript @@ -3,6 +3,6 @@ Import("env") env.NanopbProto("missing_fields") -test = env.Program(["missing_fields.c", "missing_fields.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_decode.o"]) +test = env.Program(["missing_fields.c", "missing_fields.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_decode.o", "$COMMON/pb_common.o"]) env.RunTest(test) diff --git a/tests/missing_fields/missing_fields.proto b/tests/missing_fields/missing_fields.proto index cbb23ba..cc5e550 100644 --- a/tests/missing_fields/missing_fields.proto +++ b/tests/missing_fields/missing_fields.proto @@ -1,5 +1,7 @@ /* Test for one missing field among many */ +syntax = "proto2"; + message AllFields { required int32 field1 = 1; diff --git a/tests/multiple_files/SConscript b/tests/multiple_files/SConscript index 6b4f6b6..b1281e1 100644 --- a/tests/multiple_files/SConscript +++ b/tests/multiple_files/SConscript @@ -4,10 +4,13 @@ Import("env") incpath = env.Clone() incpath.Append(PROTOCPATH = '#multiple_files') +incpath.Append(CPPPATH = '$BUILD/multiple_files') -incpath.NanopbProto("callbacks") -incpath.NanopbProto("callbacks2") -test = incpath.Program(["test_multiple_files.c", "callbacks.pb.c", "callbacks2.pb.c"]) +incpath.NanopbProto(["multifile1", "multifile1.options"]) +incpath.NanopbProto("multifile2") +incpath.NanopbProto("subdir/multifile2") +test = incpath.Program(["test_multiple_files.c", "multifile1.pb.c", + "multifile2.pb.c", "subdir/multifile2.pb.c"]) env.RunTest(test) diff --git a/tests/multiple_files/callbacks2.proto b/tests/multiple_files/callbacks2.proto deleted file mode 100644 index 9a55e15..0000000 --- a/tests/multiple_files/callbacks2.proto +++ /dev/null @@ -1,9 +0,0 @@ -// Test if including generated header file for this file + implicit include of -// callbacks.pb.h still compiles. Used with test_compiles.c. -import "callbacks.proto"; - -message Callback2Message { - required TestMessage tstmsg = 1; - required SubMessage submsg = 2; -} - diff --git a/tests/multiple_files/multifile1.options b/tests/multiple_files/multifile1.options new file mode 100644 index 0000000..c44d266 --- /dev/null +++ b/tests/multiple_files/multifile1.options @@ -0,0 +1 @@ +StaticMessage.repint32 max_count:5 diff --git a/tests/multiple_files/callbacks.proto b/tests/multiple_files/multifile1.proto index ccd1edd..18f2c67 100644 --- a/tests/multiple_files/callbacks.proto +++ b/tests/multiple_files/multifile1.proto @@ -1,3 +1,5 @@ +syntax = "proto2"; + message SubMessage { optional string stringvalue = 1; repeated int32 int32value = 2; @@ -14,3 +16,19 @@ message TestMessage { repeated string repeatedstring = 6; } +message StaticMessage { + repeated fixed32 repint32 = 1; +} + +enum SignedEnum { + SE_MIN = -128; + SE_MAX = 127; +} + +enum UnsignedEnum { + UE_MIN = 0; + UE_MAX = 255; +} + + + diff --git a/tests/multiple_files/multifile2.proto b/tests/multiple_files/multifile2.proto new file mode 100644 index 0000000..4af45fd --- /dev/null +++ b/tests/multiple_files/multifile2.proto @@ -0,0 +1,22 @@ +// Test if including generated header file for this file + implicit include of +// multifile2.pb.h still compiles. Used with test_compiles.c. +syntax = "proto2"; + +import "multifile1.proto"; + +message Callback2Message { + required TestMessage tstmsg = 1; + required SubMessage submsg = 2; +} + +message OneofMessage { + oneof msgs { + StaticMessage tstmsg = 1; + } +} + +message Enums { + required SignedEnum senum = 1; + required UnsignedEnum uenum = 2; +} + diff --git a/tests/multiple_files/subdir/multifile2.proto b/tests/multiple_files/subdir/multifile2.proto new file mode 100644 index 0000000..847a929 --- /dev/null +++ b/tests/multiple_files/subdir/multifile2.proto @@ -0,0 +1,25 @@ +syntax = "proto2"; + +package subdir; + +import "multifile1.proto"; + +message Callback2Message { + required TestMessage tstmsg = 1; + required SubMessage submsg = 2; +} + +message OneofMessage { + oneof msgs { + StaticMessage tstmsg = 1; + } +} + +message Enums { + required SignedEnum senum = 1; + required UnsignedEnum uenum = 2; +} + +message SubdirMessage { + required int32 foo = 1 [default = 15]; +} diff --git a/tests/multiple_files/test_multiple_files.c b/tests/multiple_files/test_multiple_files.c index 05722dc..70a3e59 100644 --- a/tests/multiple_files/test_multiple_files.c +++ b/tests/multiple_files/test_multiple_files.c @@ -4,9 +4,27 @@ #include <stdio.h> #include <pb_encode.h> -#include "callbacks2.pb.h" +#include "unittests.h" +#include "multifile2.pb.h" +#include "subdir/multifile2.pb.h" int main() { - return 0; + int status = 0; + + /* Test that included file options are properly loaded */ + TEST(OneofMessage_size == 27); + + /* Check that enum signedness is detected properly */ + TEST(PB_LTYPE(Enums_fields[0].type) == PB_LTYPE_VARINT); + TEST(PB_LTYPE(Enums_fields[1].type) == PB_LTYPE_UVARINT); + + /* Test that subdir file is correctly included */ + { + subdir_SubdirMessage foo = subdir_SubdirMessage_init_default; + TEST(foo.foo == 15); + /* TEST(subdir_OneofMessage_size == 27); */ /* TODO: Issue #172 */ + } + + return status; } diff --git a/tests/no_errmsg/SConscript b/tests/no_errmsg/SConscript index ed46705..629bfa6 100644 --- a/tests/no_errmsg/SConscript +++ b/tests/no_errmsg/SConscript @@ -18,10 +18,11 @@ strict = opts.Clone() strict.Append(CFLAGS = strict['CORECFLAGS']) strict.Object("pb_decode_noerr.o", "$NANOPB/pb_decode.c") strict.Object("pb_encode_noerr.o", "$NANOPB/pb_encode.c") +strict.Object("pb_common_noerr.o", "$NANOPB/pb_common.c") # Now build and run the test normally. -enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_noerr.o"]) -dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_noerr.o"]) +enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_noerr.o", "pb_common_noerr.o"]) +dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_noerr.o", "pb_common_noerr.o"]) env.RunTest(enc) env.RunTest([dec, "encode_alltypes.output"]) diff --git a/tests/no_messages/no_messages.proto b/tests/no_messages/no_messages.proto index 279216b..45bb2e6 100644 --- a/tests/no_messages/no_messages.proto +++ b/tests/no_messages/no_messages.proto @@ -1,5 +1,7 @@ /* Test that a file without any messages works. */ +syntax = "proto2"; + enum Test { First = 1; } diff --git a/tests/oneof/SConscript b/tests/oneof/SConscript new file mode 100644 index 0000000..928ce63 --- /dev/null +++ b/tests/oneof/SConscript @@ -0,0 +1,33 @@ +# Test the 'oneof' feature for generating C unions. + +Import('env') + +import re + +match = None +if 'PROTOC_VERSION' in env: + match = re.search('([0-9]+).([0-9]+).([0-9]+)', env['PROTOC_VERSION']) + +if match: + version = list(map(int, match.groups())) + +# Oneof is supported by protoc >= 2.6.0 +if env.GetOption('clean') or (match and (version[0] > 2 or (version[0] == 2 and version[1] >= 6))): + env.NanopbProto('oneof') + + enc = env.Program(['encode_oneof.c', + 'oneof.pb.c', + '$COMMON/pb_encode.o', + '$COMMON/pb_common.o']) + + dec = env.Program(['decode_oneof.c', + 'oneof.pb.c', + '$COMMON/pb_decode.o', + '$COMMON/pb_common.o']) + + env.RunTest("message1.pb", enc, ARGS = ['1']) + env.RunTest("message1.txt", [dec, 'message1.pb'], ARGS = ['1']) + env.RunTest("message2.pb", enc, ARGS = ['2']) + env.RunTest("message2.txt", [dec, 'message2.pb'], ARGS = ['2']) + env.RunTest("message3.pb", enc, ARGS = ['3']) + env.RunTest("message3.txt", [dec, 'message3.pb'], ARGS = ['3']) diff --git a/tests/oneof/decode_oneof.c b/tests/oneof/decode_oneof.c new file mode 100644 index 0000000..37075cd --- /dev/null +++ b/tests/oneof/decode_oneof.c @@ -0,0 +1,131 @@ +/* Decode a message using oneof fields */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pb_decode.h> +#include "oneof.pb.h" +#include "test_helpers.h" +#include "unittests.h" + +/* Test the 'OneOfMessage' */ +int test_oneof_1(pb_istream_t *stream, int option) +{ + OneOfMessage msg; + int status = 0; + + /* To better catch initialization errors */ + memset(&msg, 0xAA, sizeof(msg)); + + if (!pb_decode(stream, OneOfMessage_fields, &msg)) + { + printf("Decoding failed: %s\n", PB_GET_ERROR(stream)); + return 1; + } + + /* Check that the basic fields work normally */ + TEST(msg.prefix == 123); + TEST(msg.suffix == 321); + + /* Check that we got the right oneof according to command line */ + if (option == 1) + { + TEST(msg.which_values == OneOfMessage_first_tag); + TEST(msg.values.first == 999); + } + else if (option == 2) + { + TEST(msg.which_values == OneOfMessage_second_tag); + TEST(strcmp(msg.values.second, "abcd") == 0); + } + else if (option == 3) + { + TEST(msg.which_values == OneOfMessage_third_tag); + TEST(msg.values.third.array[0] == 1); + TEST(msg.values.third.array[1] == 2); + TEST(msg.values.third.array[2] == 3); + TEST(msg.values.third.array[3] == 4); + TEST(msg.values.third.array[4] == 5); + } + + return status; +} + + +/* Test the 'PlainOneOfMessage' */ +int test_oneof_2(pb_istream_t *stream, int option) +{ + PlainOneOfMessage msg = PlainOneOfMessage_init_zero; + int status = 0; + + if (!pb_decode(stream, PlainOneOfMessage_fields, &msg)) + { + printf("Decoding failed: %s\n", PB_GET_ERROR(stream)); + return 1; + } + + /* Check that we got the right oneof according to command line */ + if (option == 1) + { + TEST(msg.which_values == OneOfMessage_first_tag); + TEST(msg.values.first == 999); + } + else if (option == 2) + { + TEST(msg.which_values == OneOfMessage_second_tag); + TEST(strcmp(msg.values.second, "abcd") == 0); + } + else if (option == 3) + { + TEST(msg.which_values == OneOfMessage_third_tag); + TEST(msg.values.third.array[0] == 1); + TEST(msg.values.third.array[1] == 2); + TEST(msg.values.third.array[2] == 3); + TEST(msg.values.third.array[3] == 4); + TEST(msg.values.third.array[4] == 5); + } + + return status; +} + +int main(int argc, char **argv) +{ + uint8_t buffer[OneOfMessage_size]; + size_t count; + int option; + + if (argc != 2) + { + fprintf(stderr, "Usage: decode_oneof [number]\n"); + return 1; + } + option = atoi(argv[1]); + + SET_BINARY_MODE(stdin); + count = fread(buffer, 1, sizeof(buffer), stdin); + + if (!feof(stdin)) + { + printf("Message does not fit in buffer\n"); + return 1; + } + + { + int status = 0; + pb_istream_t stream; + + stream = pb_istream_from_buffer(buffer, count); + status = test_oneof_1(&stream, option); + + if (status != 0) + return status; + + stream = pb_istream_from_buffer(buffer, count); + status = test_oneof_2(&stream, option); + + if (status != 0) + return status; + } + + return 0; +} diff --git a/tests/oneof/encode_oneof.c b/tests/oneof/encode_oneof.c new file mode 100644 index 0000000..913d2d4 --- /dev/null +++ b/tests/oneof/encode_oneof.c @@ -0,0 +1,64 @@ +/* Encode a message using oneof fields */ + +#include <stdio.h> +#include <stdlib.h> +#include <pb_encode.h> +#include "oneof.pb.h" +#include "test_helpers.h" + +int main(int argc, char **argv) +{ + uint8_t buffer[OneOfMessage_size]; + OneOfMessage msg = OneOfMessage_init_zero; + pb_ostream_t stream; + int option; + + if (argc != 2) + { + fprintf(stderr, "Usage: encode_oneof [number]\n"); + return 1; + } + option = atoi(argv[1]); + + /* Prefix and suffix are used to test that the union does not disturb + * other fields in the same message. */ + msg.prefix = 123; + + /* We encode one of the 'values' fields based on command line argument */ + if (option == 1) + { + msg.which_values = OneOfMessage_first_tag; + msg.values.first = 999; + } + else if (option == 2) + { + msg.which_values = OneOfMessage_second_tag; + strcpy(msg.values.second, "abcd"); + } + else if (option == 3) + { + msg.which_values = OneOfMessage_third_tag; + msg.values.third.array_count = 5; + msg.values.third.array[0] = 1; + msg.values.third.array[1] = 2; + msg.values.third.array[2] = 3; + msg.values.third.array[3] = 4; + msg.values.third.array[4] = 5; + } + + msg.suffix = 321; + + stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + + if (pb_encode(&stream, OneOfMessage_fields, &msg)) + { + SET_BINARY_MODE(stdout); + fwrite(buffer, 1, stream.bytes_written, stdout); + return 0; + } + else + { + fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream)); + return 1; + } +} diff --git a/tests/oneof/oneof.proto b/tests/oneof/oneof.proto new file mode 100644 index 0000000..b4fe56f --- /dev/null +++ b/tests/oneof/oneof.proto @@ -0,0 +1,32 @@ +syntax = "proto2"; + +import 'nanopb.proto'; + +message SubMessage +{ + repeated int32 array = 1 [(nanopb).max_count = 8]; +} + +/* Oneof in a message with other fields */ +message OneOfMessage +{ + required int32 prefix = 1; + oneof values + { + int32 first = 5; + string second = 6 [(nanopb).max_size = 8]; + SubMessage third = 7; + } + required int32 suffix = 99; +} + +/* Oneof in a message by itself */ +message PlainOneOfMessage +{ + oneof values + { + int32 first = 5; + string second = 6 [(nanopb).max_size = 8]; + SubMessage third = 7; + } +}
\ No newline at end of file diff --git a/tests/options/SConscript b/tests/options/SConscript index 89a00fa..215e3bd 100644 --- a/tests/options/SConscript +++ b/tests/options/SConscript @@ -4,6 +4,9 @@ Import("env") env.NanopbProto("options") env.Object('options.pb.c') - env.Match(['options.pb.h', 'options.expected']) +env.NanopbProto("proto3_options") +env.Object('proto3_options.pb.c') +env.Match(['proto3_options.pb.h', 'proto3_options.expected']) + diff --git a/tests/options/options.expected b/tests/options/options.expected index e6179a2..9e47e6a 100644 --- a/tests/options/options.expected +++ b/tests/options/options.expected @@ -1,7 +1,20 @@ char filesize\[20\]; char msgsize\[30\]; char fieldsize\[40\]; +char fieldlen\[41\]; pb_callback_t int32_callback; \sEnumValue1 = 1 Message5_EnumValue1 } pb_packed my_packed_struct; +! skipped_field +! SkippedMessage +#define PB_MSG_103 Message3 +#define PB_MSG_104 Message4 +#define PB_MSG_105 Message5 +#define OPTIONS_MESSAGES \\ +\s+PB_MSG\(103,[0-9]*,Message3\) \\ +\s+PB_MSG\(104,-1,Message4\) \\ +\s+PB_MSG\(105,[0-9]*,Message5\) \\ +#define Message5_msgid 105 +! has_proto3field + diff --git a/tests/options/options.proto b/tests/options/options.proto index b5badcf..c6ca5e2 100644 --- a/tests/options/options.proto +++ b/tests/options/options.proto @@ -2,6 +2,8 @@ * options.expected lists the patterns that are searched for in the output. */ +syntax = "proto2"; + import "nanopb.proto"; // File level options @@ -22,12 +24,15 @@ message Message2 // Field level options message Message3 { + option (nanopb_msgopt).msgid = 103; required string fieldsize = 1 [(nanopb).max_size = 40]; + required string fieldlen = 2 [(nanopb).max_length = 40]; } // Forced callback field message Message4 { + option (nanopb_msgopt).msgid = 104; required int32 int32_callback = 1 [(nanopb).type = FT_CALLBACK]; } @@ -47,6 +52,7 @@ message EnumTest // Short enum names inside message message Message5 { + option (nanopb_msgopt).msgid = 105; enum Enum2 { option (nanopb_enumopt).long_names = false; @@ -63,11 +69,30 @@ message my_packed_struct } // Message with ignored field -// Note: doesn't really test if the field is missing in the output, -// but atleast tests that the output compiles. message Message6 { required int32 field1 = 1; - optional int32 field2 = 2 [(nanopb).type = FT_IGNORE]; + optional int32 skipped_field = 2 [(nanopb).type = FT_IGNORE]; +} + +// Message that is skipped +message SkippedMessage +{ + option (nanopb_msgopt).skip_message = true; + required int32 foo = 1; +} + +// Message with oneof field +message OneofMessage +{ + oneof foo { + int32 bar = 1; + } +} + +// Proto3-style optional field in proto2 file +message Proto3Field +{ + optional int32 proto3field = 1 [(nanopb).proto3 = true]; } diff --git a/tests/options/proto3_options.expected b/tests/options/proto3_options.expected new file mode 100644 index 0000000..cc2f29c --- /dev/null +++ b/tests/options/proto3_options.expected @@ -0,0 +1,4 @@ +! bool has_proto3_default +bool has_proto3_off +! bool has_proto3_on + diff --git a/tests/options/proto3_options.proto b/tests/options/proto3_options.proto new file mode 100644 index 0000000..1017f04 --- /dev/null +++ b/tests/options/proto3_options.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +import "nanopb.proto"; + +message Message1 +{ + int32 proto3_default = 1; + int32 proto3_off = 2 [(nanopb).proto3 = false]; + int32 proto3_on = 3 [(nanopb).proto3 = true]; +} + diff --git a/tests/package_name/SConscript b/tests/package_name/SConscript index 8f1b902..4afc503 100644 --- a/tests/package_name/SConscript +++ b/tests/package_name/SConscript @@ -3,14 +3,16 @@ Import("env") -# Build a modified alltypes.proto -def modify_proto(target, source, env): - '''Add a "package test.package;" directive to the beginning of the .proto file.''' - data = open(str(source[0]), 'r').read() - open(str(target[0]), 'w').write("package test.package;\n\n" + data) - return 0 +def set_pkgname(src, dst, pkgname): + data = open(str(src)).read() + placeholder = '// package name placeholder' + assert placeholder in data + data = data.replace(placeholder, 'package %s;' % pkgname) + open(str(dst), 'w').write(data) -env.Command("alltypes.proto", "#alltypes/alltypes.proto", modify_proto) +# Build a modified alltypes.proto +env.Command("alltypes.proto", "#alltypes/alltypes.proto", + lambda target, source, env: set_pkgname(source[0], target[0], 'test.package')) env.Command("alltypes.options", "#alltypes/alltypes.options", Copy("$TARGET", "$SOURCE")) env.NanopbProto(["alltypes", "alltypes.options"]) @@ -29,7 +31,7 @@ def modify_c(target, source, env): env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", modify_c) # Encode and compare results to original alltypes testcase -enc = env.Program(["encode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_encode.o"]) +enc = env.Program(["encode_alltypes.c", "alltypes.pb.c", "$COMMON/pb_encode.o", "$COMMON/pb_common.o"]) refdec = "$BUILD/alltypes/decode_alltypes$PROGSUFFIX" env.RunTest(enc) env.Compare(["encode_alltypes.output", "$BUILD/alltypes/encode_alltypes.output"]) diff --git a/tests/regression/issue_118/SConscript b/tests/regression/issue_118/SConscript new file mode 100644 index 0000000..833d9de --- /dev/null +++ b/tests/regression/issue_118/SConscript @@ -0,0 +1,12 @@ +# Regression test for Issue 118: Short enum names in imported proto files are not honoured + +Import("env") +env = env.Clone() +env.Append(PROTOCPATH = "#regression/issue_118") + +env.NanopbProto("enumdef") +env.Object('enumdef.pb.c') + +env.NanopbProto(["enumuse", "enumdef.proto"]) +env.Object('enumuse.pb.c') + diff --git a/tests/regression/issue_118/enumdef.proto b/tests/regression/issue_118/enumdef.proto new file mode 100644 index 0000000..46845bc --- /dev/null +++ b/tests/regression/issue_118/enumdef.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +import 'nanopb.proto'; + +enum MyEnum { + option (nanopb_enumopt).long_names = false; + FOOBAR = 1; +} diff --git a/tests/regression/issue_118/enumuse.proto b/tests/regression/issue_118/enumuse.proto new file mode 100644 index 0000000..4afc452 --- /dev/null +++ b/tests/regression/issue_118/enumuse.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +import 'enumdef.proto'; + +message MyMessage { + required MyEnum myenum = 1 [default = FOOBAR]; +} diff --git a/tests/regression/issue_125/SConscript b/tests/regression/issue_125/SConscript new file mode 100644 index 0000000..f2155e6 --- /dev/null +++ b/tests/regression/issue_125/SConscript @@ -0,0 +1,9 @@ +# Regression test for Issue 125: Wrong identifier name for extension fields + +Import("env") + +env.NanopbProto(["extensionbug", "extensionbug.options"]) +env.Object('extensionbug.pb.c') + +env.Match(['extensionbug.pb.h', 'extensionbug.expected']) + diff --git a/tests/regression/issue_125/extensionbug.expected b/tests/regression/issue_125/extensionbug.expected new file mode 100644 index 0000000..fc21335 --- /dev/null +++ b/tests/regression/issue_125/extensionbug.expected @@ -0,0 +1,3 @@ +pb_extension_type_t Message2_extras +uint32_t field2 + diff --git a/tests/regression/issue_125/extensionbug.options b/tests/regression/issue_125/extensionbug.options new file mode 100644 index 0000000..30b464a --- /dev/null +++ b/tests/regression/issue_125/extensionbug.options @@ -0,0 +1,4 @@ +* type:FT_IGNORE + +Message2.extras type:FT_STATIC +Message2.field2 type:FT_STATIC diff --git a/tests/regression/issue_125/extensionbug.proto b/tests/regression/issue_125/extensionbug.proto new file mode 100644 index 0000000..fd1e74f --- /dev/null +++ b/tests/regression/issue_125/extensionbug.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + +message Message1 +{ + optional uint32 fieldA = 1; + extensions 30 to max; +} + +message Message2 +{ + extend Message1 + { + optional Message2 extras = 30; + } + + optional uint32 field1 = 1; + optional uint32 field2 = 2; +} diff --git a/tests/regression/issue_141/SConscript b/tests/regression/issue_141/SConscript new file mode 100644 index 0000000..b6526be --- /dev/null +++ b/tests/regression/issue_141/SConscript @@ -0,0 +1,8 @@ +# Regression test for issue 141: wrong encoded size #define for oneof messages + +Import("env") + +env.NanopbProto("testproto") +env.Object('testproto.pb.c') +env.Match(['testproto.pb.h', 'testproto.expected']) + diff --git a/tests/regression/issue_141/testproto.expected b/tests/regression/issue_141/testproto.expected new file mode 100644 index 0000000..75bc195 --- /dev/null +++ b/tests/regression/issue_141/testproto.expected @@ -0,0 +1,7 @@ +define SubMessage_size \s* 88 +define OneOfMessage_size \s* 113 +define topMessage_size \s* 70 +define MyMessage1_size \s* 46 +define MyMessage2_size \s* 8 +define MyMessage3_size \s* 5 +define MyMessage4_size \s* 18 diff --git a/tests/regression/issue_141/testproto.proto b/tests/regression/issue_141/testproto.proto new file mode 100644 index 0000000..a445c68 --- /dev/null +++ b/tests/regression/issue_141/testproto.proto @@ -0,0 +1,52 @@ +syntax = "proto2"; + +import 'nanopb.proto'; + +message SubMessage +{ + repeated int32 array = 1 [(nanopb).max_count = 8]; +} + +message OneOfMessage +{ + required int32 prefix = 1; + oneof values + { + int32 first = 5; + string second = 6 [(nanopb).max_size = 8]; + SubMessage third = 7; + } + required int32 suffix = 99; +} + +message topMessage { + required int32 start = 1; + oneof msg { + MyMessage1 msg1 = 2; + MyMessage2 msg2 = 3; + } + required int32 end = 4; +} + +message MyMessage1 { + required uint32 n1 = 1; + required uint32 n2 = 2; + required string s = 3 [(nanopb).max_size = 32]; +} + +message MyMessage2 { + required uint32 num = 1; + required bool b = 2; +} + +message MyMessage3 { + required bool bbb = 1; + required string ss = 2 [(nanopb).max_size = 1]; +} + +message MyMessage4 { + required bool bbbb = 1; + required string sss = 2 [(nanopb).max_size = 2]; + required uint32 num = 3; + required uint32 num2 = 4; +} diff --git a/tests/regression/issue_145/SConscript b/tests/regression/issue_145/SConscript new file mode 100644 index 0000000..0b793a7 --- /dev/null +++ b/tests/regression/issue_145/SConscript @@ -0,0 +1,9 @@ +# Regression test for Issue 145: Allow /* */ and // comments in .options files + +Import("env") + +env.NanopbProto(["comments", "comments.options"]) +env.Object('comments.pb.c') + +env.Match(['comments.pb.h', 'comments.expected']) + diff --git a/tests/regression/issue_145/comments.expected b/tests/regression/issue_145/comments.expected new file mode 100644 index 0000000..7f87458 --- /dev/null +++ b/tests/regression/issue_145/comments.expected @@ -0,0 +1,3 @@ +char foo\[5\]; +char bar\[16\]; + diff --git a/tests/regression/issue_145/comments.options b/tests/regression/issue_145/comments.options new file mode 100644 index 0000000..89959ba --- /dev/null +++ b/tests/regression/issue_145/comments.options @@ -0,0 +1,6 @@ +/* Block comment */ +# Line comment +// Line comment +DummyMessage.foo /* Block comment */ max_size:5 +DummyMessage.bar max_size:16 # Line comment ### + diff --git a/tests/regression/issue_145/comments.proto b/tests/regression/issue_145/comments.proto new file mode 100644 index 0000000..621779f --- /dev/null +++ b/tests/regression/issue_145/comments.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; + +message DummyMessage { + required string foo = 1; + required string bar = 2; +} + diff --git a/tests/regression/issue_166/SConscript b/tests/regression/issue_166/SConscript new file mode 100644 index 0000000..c50b919 --- /dev/null +++ b/tests/regression/issue_166/SConscript @@ -0,0 +1,13 @@ +# Verify that the maximum encoded size is calculated properly +# for enums. + +Import('env') + +env.NanopbProto('enums') + +p = env.Program(["enum_encoded_size.c", + "enums.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) +env.RunTest(p) + diff --git a/tests/regression/issue_166/enum_encoded_size.c b/tests/regression/issue_166/enum_encoded_size.c new file mode 100644 index 0000000..84e1c7d --- /dev/null +++ b/tests/regression/issue_166/enum_encoded_size.c @@ -0,0 +1,43 @@ +#include <stdio.h> +#include <string.h> +#include <pb_encode.h> +#include "unittests.h" +#include "enums.pb.h" + +int main() +{ + int status = 0; + + uint8_t buf[256]; + SignedMsg msg1; + UnsignedMsg msg2; + pb_ostream_t s; + + { + COMMENT("Test negative value of signed enum"); + /* Negative value should take up the maximum size */ + msg1.value = SignedEnum_SE_MIN; + s = pb_ostream_from_buffer(buf, sizeof(buf)); + TEST(pb_encode(&s, SignedMsg_fields, &msg1)); + TEST(s.bytes_written == SignedMsg_size); + + COMMENT("Test positive value of signed enum"); + /* Positive value should be smaller */ + msg1.value = SignedEnum_SE_MAX; + s = pb_ostream_from_buffer(buf, sizeof(buf)); + TEST(pb_encode(&s, SignedMsg_fields, &msg1)); + TEST(s.bytes_written < SignedMsg_size); + } + + { + COMMENT("Test positive value of unsigned enum"); + /* This should take up the maximum size */ + msg2.value = UnsignedEnum_UE_MAX; + s = pb_ostream_from_buffer(buf, sizeof(buf)); + TEST(pb_encode(&s, UnsignedMsg_fields, &msg2)); + TEST(s.bytes_written == UnsignedMsg_size); + } + + return status; +} + diff --git a/tests/regression/issue_166/enums.proto b/tests/regression/issue_166/enums.proto new file mode 100644 index 0000000..3694804 --- /dev/null +++ b/tests/regression/issue_166/enums.proto @@ -0,0 +1,18 @@ +syntax = "proto2"; + +enum SignedEnum { + SE_MIN = -1; + SE_MAX = 255; +} + +enum UnsignedEnum { + UE_MAX = 65536; +} + +message SignedMsg { + required SignedEnum value = 1; +} + +message UnsignedMsg { + required UnsignedEnum value = 1; +} diff --git a/tests/regression/issue_172/SConscript b/tests/regression/issue_172/SConscript new file mode 100644 index 0000000..49c919e --- /dev/null +++ b/tests/regression/issue_172/SConscript @@ -0,0 +1,16 @@ +# Verify that _size define is generated for messages that have +# includes from another directory. + +Import('env') + +incpath = env.Clone() +incpath.Append(PROTOCPATH="#regression/issue_172/submessage") +incpath.Append(CPPPATH="$BUILD/regression/issue_172/submessage") +incpath.NanopbProto('test') +incpath.NanopbProto(['submessage/submessage', 'submessage/submessage.options']) + +p = incpath.Program(["msg_size.c", + "test.pb.c", + "submessage/submessage.pb.c"]) + + diff --git a/tests/regression/issue_172/msg_size.c b/tests/regression/issue_172/msg_size.c new file mode 100644 index 0000000..be45acb --- /dev/null +++ b/tests/regression/issue_172/msg_size.c @@ -0,0 +1,9 @@ +#include "test.pb.h" + +PB_STATIC_ASSERT(testmessage_size >= 1+1+1+1+16, TESTMESSAGE_SIZE_IS_WRONG) + +int main() +{ + return 0; +} + diff --git a/tests/regression/issue_172/submessage/submessage.options b/tests/regression/issue_172/submessage/submessage.options new file mode 100644 index 0000000..12fb198 --- /dev/null +++ b/tests/regression/issue_172/submessage/submessage.options @@ -0,0 +1 @@ +submessage.data max_size: 16 diff --git a/tests/regression/issue_172/submessage/submessage.proto b/tests/regression/issue_172/submessage/submessage.proto new file mode 100644 index 0000000..ce6804a --- /dev/null +++ b/tests/regression/issue_172/submessage/submessage.proto @@ -0,0 +1,4 @@ +syntax = "proto2"; +message submessage { + required bytes data = 1; +} diff --git a/tests/regression/issue_172/test.proto b/tests/regression/issue_172/test.proto new file mode 100644 index 0000000..fbd97be --- /dev/null +++ b/tests/regression/issue_172/test.proto @@ -0,0 +1,6 @@ +syntax = "proto2"; +import "submessage.proto"; + +message testmessage { + optional submessage sub = 1; +} diff --git a/tests/regression/issue_188/SConscript b/tests/regression/issue_188/SConscript new file mode 100644 index 0000000..6bc3271 --- /dev/null +++ b/tests/regression/issue_188/SConscript @@ -0,0 +1,6 @@ +# Regression test for issue with Enums inside OneOf. + +Import('env') + +env.NanopbProto('oneof') + diff --git a/tests/regression/issue_188/oneof.proto b/tests/regression/issue_188/oneof.proto new file mode 100644 index 0000000..e37f5c0 --- /dev/null +++ b/tests/regression/issue_188/oneof.proto @@ -0,0 +1,29 @@ +syntax = "proto2"; + +message MessageOne +{ + required uint32 one = 1; + required uint32 two = 2; + required uint32 three = 3; + required int32 four = 4; +} + +enum EnumTwo +{ + SOME_ENUM_1 = 1; + SOME_ENUM_2 = 5; + SOME_ENUM_3 = 6; + SOME_ENUM_4 = 9; + SOME_ENUM_5 = 10; + SOME_ENUM_6 = 12; + SOME_ENUM_7 = 39; + SOME_ENUM_8 = 401; +} + +message OneofMessage +{ + oneof payload { + MessageOne message = 1; + EnumTwo enum = 2; + } +} diff --git a/tests/regression/issue_195/SConscript b/tests/regression/issue_195/SConscript new file mode 100644 index 0000000..78326d3 --- /dev/null +++ b/tests/regression/issue_195/SConscript @@ -0,0 +1,10 @@ +# Regression test for Issue 195: Message size not calculated if a submessage includes +# bytes. Basically a non-working #define being generated. + +Import("env") + +env.NanopbProto(["test"]) +env.Object('test.pb.c') + +env.Match(['test.pb.h', 'test.expected']) + diff --git a/tests/regression/issue_195/test.expected b/tests/regression/issue_195/test.expected new file mode 100644 index 0000000..83ea7ab --- /dev/null +++ b/tests/regression/issue_195/test.expected @@ -0,0 +1 @@ +/\* TestMessage_size depends diff --git a/tests/regression/issue_195/test.proto b/tests/regression/issue_195/test.proto new file mode 100644 index 0000000..7a77d69 --- /dev/null +++ b/tests/regression/issue_195/test.proto @@ -0,0 +1,8 @@ +message TestMessage { + required uint32 id = 1; + required bytes payload = 2; +} +message EncapsulatedMessage { + required uint32 id = 1; + required TestMessage test = 2; +} diff --git a/tests/regression/issue_203/SConscript b/tests/regression/issue_203/SConscript new file mode 100644 index 0000000..8b4d6cc --- /dev/null +++ b/tests/regression/issue_203/SConscript @@ -0,0 +1,9 @@ +# Regression test for issue with multiple files generated at once + +Import('env') + +env.Command(['file1.pb.c', 'file1.pb.h', 'file2.pb.c', 'file2.pb.h'], ['file1.proto', 'file2.proto'], + env['NANOPB_PROTO_CMD']) + +env.Object('file1.pb.c') +env.Object('file2.pb.c') diff --git a/tests/regression/issue_203/file1.proto b/tests/regression/issue_203/file1.proto new file mode 100644 index 0000000..dae250b --- /dev/null +++ b/tests/regression/issue_203/file1.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +message SubMessage1 { + required int32 foo = 1; +} + +message Message1 { + required SubMessage1 bar = 1; +} + diff --git a/tests/regression/issue_203/file2.proto b/tests/regression/issue_203/file2.proto new file mode 100644 index 0000000..513b0f0 --- /dev/null +++ b/tests/regression/issue_203/file2.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +message SubMessage2 { + required int32 foo = 1; +} + +message Message2 { + required SubMessage2 bar = 1; +} + diff --git a/tests/regression/issue_205/SConscript b/tests/regression/issue_205/SConscript new file mode 100644 index 0000000..ed8899d --- /dev/null +++ b/tests/regression/issue_205/SConscript @@ -0,0 +1,14 @@ +# Check that pb_release() correctly handles corrupted size fields of +# static arrays. + +Import('env', 'malloc_env') + +env.NanopbProto('size_corruption') + +p = malloc_env.Program(["size_corruption.c", + "size_corruption.pb.c", + "$COMMON/pb_decode_with_malloc.o", + "$COMMON/pb_common_with_malloc.o", + "$COMMON/malloc_wrappers.o"]) +env.RunTest(p) + diff --git a/tests/regression/issue_205/size_corruption.c b/tests/regression/issue_205/size_corruption.c new file mode 100644 index 0000000..08cef45 --- /dev/null +++ b/tests/regression/issue_205/size_corruption.c @@ -0,0 +1,12 @@ +#include "size_corruption.pb.h" +#include <pb_decode.h> + +int main() +{ + MainMessage msg = MainMessage_init_zero; + msg.bar_count = (pb_size_t)-1; + pb_release(MainMessage_fields, &msg); + + return 0; +} + diff --git a/tests/regression/issue_205/size_corruption.proto b/tests/regression/issue_205/size_corruption.proto new file mode 100644 index 0000000..6c9c245 --- /dev/null +++ b/tests/regression/issue_205/size_corruption.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; +import 'nanopb.proto'; + +message SubMessage { + repeated int32 foo = 1 [(nanopb).type = FT_POINTER]; +} + +message MainMessage { + repeated SubMessage bar = 1 [(nanopb).max_count = 5]; +} + diff --git a/tests/regression/issue_227/SConscript b/tests/regression/issue_227/SConscript new file mode 100644 index 0000000..1074124 --- /dev/null +++ b/tests/regression/issue_227/SConscript @@ -0,0 +1,14 @@ +# Regression test for Issue 227:Using proto3 type fields can cause unaligned access +# NOTE: This test will only detect problems when run with clang sanitizer (which +# is done regularly by a jenkins run). + +Import('env') + +env.NanopbProto('unaligned_uint64') + +p = env.Program(["unaligned_uint64.c", + "unaligned_uint64.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) +env.RunTest(p) + diff --git a/tests/regression/issue_227/unaligned_uint64.c b/tests/regression/issue_227/unaligned_uint64.c new file mode 100644 index 0000000..17c1d77 --- /dev/null +++ b/tests/regression/issue_227/unaligned_uint64.c @@ -0,0 +1,14 @@ +#include "unaligned_uint64.pb.h" +#include <pb_encode.h> + +int main() +{ + uint8_t buf[128]; + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + MainMessage msg = MainMessage_init_zero; + msg.bar[0] = 'A'; + pb_encode(&stream, MainMessage_fields, &msg); + + return 0; +} + diff --git a/tests/regression/issue_227/unaligned_uint64.proto b/tests/regression/issue_227/unaligned_uint64.proto new file mode 100644 index 0000000..f0269f6 --- /dev/null +++ b/tests/regression/issue_227/unaligned_uint64.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +import 'nanopb.proto'; + +message MainMessage { + string foo = 1 [(nanopb).max_size = 3]; + string bar = 2 [(nanopb).max_size = 8]; +} + diff --git a/tests/regression/issue_229/SConscript b/tests/regression/issue_229/SConscript new file mode 100644 index 0000000..b0f8376 --- /dev/null +++ b/tests/regression/issue_229/SConscript @@ -0,0 +1,13 @@ +# Regression test for Issue 229: problem encoding message that has +# multiple oneof fields +Import('env') + +env.NanopbProto('multiple_oneof') + +p = env.Program(["multiple_oneof.c", + "multiple_oneof.pb.c", + "$COMMON/pb_decode.o", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) +env.RunTest(p) + diff --git a/tests/regression/issue_229/multiple_oneof.c b/tests/regression/issue_229/multiple_oneof.c new file mode 100644 index 0000000..902248d --- /dev/null +++ b/tests/regression/issue_229/multiple_oneof.c @@ -0,0 +1,35 @@ +#include "multiple_oneof.pb.h" +#include <unittests.h> +#include <pb_encode.h> +#include <pb_decode.h> + +int main() +{ + int status = 0; + uint8_t buf[128]; + size_t msglen; + + { + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + MainMessage msg = MainMessage_init_zero; + msg.which_oneof1 = MainMessage_oneof1_uint32_tag; + msg.oneof1.oneof1_uint32 = 1234; + msg.which_oneof2 = MainMessage_oneof2_uint32_tag; + msg.oneof2.oneof2_uint32 = 5678; + TEST(pb_encode(&stream, MainMessage_fields, &msg)); + msglen = stream.bytes_written; + } + + { + pb_istream_t stream = pb_istream_from_buffer(buf, msglen); + MainMessage msg = MainMessage_init_zero; + TEST(pb_decode(&stream, MainMessage_fields, &msg)); + TEST(msg.which_oneof1 == MainMessage_oneof1_uint32_tag); + TEST(msg.oneof1.oneof1_uint32 == 1234); + TEST(msg.which_oneof2 == MainMessage_oneof2_uint32_tag); + TEST(msg.oneof2.oneof2_uint32 == 5678); + } + + return status; +} + diff --git a/tests/regression/issue_229/multiple_oneof.proto b/tests/regression/issue_229/multiple_oneof.proto new file mode 100644 index 0000000..22373e1 --- /dev/null +++ b/tests/regression/issue_229/multiple_oneof.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; + +message MainMessage { + oneof oneof1 { + uint32 oneof1_uint32 = 1; + } + oneof oneof2 { + uint32 oneof2_uint32 = 2; + } +} + diff --git a/tests/regression/issue_242/SConscript b/tests/regression/issue_242/SConscript new file mode 100644 index 0000000..000063e --- /dev/null +++ b/tests/regression/issue_242/SConscript @@ -0,0 +1,13 @@ +# Regression test for Issue 242: pb_encode does not encode tag for +# extension fields that is all zeros +Import('env') + +env.NanopbProto('zero_value') + +p = env.Program(["zero_value.c", + "zero_value.pb.c", + "$COMMON/pb_decode.o", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) +env.RunTest(p) + diff --git a/tests/regression/issue_242/zero_value.c b/tests/regression/issue_242/zero_value.c new file mode 100644 index 0000000..b3d96b7 --- /dev/null +++ b/tests/regression/issue_242/zero_value.c @@ -0,0 +1,51 @@ +#include <unittests.h> +#include <pb_encode.h> +#include <pb_decode.h> +#include <string.h> +#include "zero_value.pb.h" + +int main() +{ + int status = 0; + + COMMENT("Test extension fields with zero values"); + { + uint8_t buffer[256] = {0}; + pb_ostream_t ostream; + int32_t value = 0; + Extendable source = {0}; + + pb_extension_t source_ext = {0}; + source_ext.type = &opt_int32; + source_ext.dest = &value; + source.extensions = &source_ext; + + ostream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + TEST(pb_encode(&ostream, Extendable_fields, &source)); + + TEST(ostream.bytes_written == 2); + TEST(memcmp(buffer, "\x58\x00", 2) == 0); + } + + /* Note: There never was a bug here, but this check is included + * in the regression test because the logic is closely related. + */ + COMMENT("Test pointer fields with zero values"); + { + uint8_t buffer[256] = {0}; + pb_ostream_t ostream; + int32_t value = 0; + PointerMessage source = {0}; + + source.opt_int32 = &value; + + ostream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + TEST(pb_encode(&ostream, PointerMessage_fields, &source)); + + TEST(ostream.bytes_written == 2); + TEST(memcmp(buffer, "\x58\x00", 2) == 0); + } + + return status; +} + diff --git a/tests/regression/issue_242/zero_value.proto b/tests/regression/issue_242/zero_value.proto new file mode 100644 index 0000000..020a39a --- /dev/null +++ b/tests/regression/issue_242/zero_value.proto @@ -0,0 +1,15 @@ +syntax = "proto2"; +import "nanopb.proto"; + +message Extendable { + extensions 10 to 100; +} + +extend Extendable { + optional int32 opt_int32 = 11; +} + +message PointerMessage { + optional int32 opt_int32 = 11 [(nanopb).type = FT_POINTER]; +} + diff --git a/tests/regression/issue_247/SConscript b/tests/regression/issue_247/SConscript new file mode 100644 index 0000000..b41e9f2 --- /dev/null +++ b/tests/regression/issue_247/SConscript @@ -0,0 +1,14 @@ +# Test that pb_check_proto3_default_value() correctly skips padding +# bytes in submessage structures. + +Import("env") + +env.NanopbProto("padding") + +p = env.Program(["padding.c", + "padding.pb.c", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(p) + diff --git a/tests/regression/issue_247/padding.c b/tests/regression/issue_247/padding.c new file mode 100644 index 0000000..8860179 --- /dev/null +++ b/tests/regression/issue_247/padding.c @@ -0,0 +1,32 @@ +#include <pb_encode.h> +#include <unittests.h> +#include <string.h> +#include "padding.pb.h" + +int main() +{ + int status = 0; + + TestMessage msg; + + /* Set padding bytes to garbage */ + memset(&msg, 0xAA, sizeof(msg)); + + /* Set all meaningful fields to 0 */ + msg.submsg.boolfield = false; + msg.submsg.intfield = 0; + + /* Test encoding */ + { + pb_byte_t buf[128] = {0}; + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + TEST(pb_encode(&stream, TestMessage_fields, &msg)); + + /* Because all fields have zero values, proto3 encoder + * shouldn't write out anything. */ + TEST(stream.bytes_written == 0); + } + + return status; +} + diff --git a/tests/regression/issue_247/padding.proto b/tests/regression/issue_247/padding.proto new file mode 100644 index 0000000..20bddac --- /dev/null +++ b/tests/regression/issue_247/padding.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +import "nanopb.proto"; + +message SubMessage { + bool boolfield = 1; + int64 intfield = 2; +} + +message TestMessage { + SubMessage submsg = 1; +} + diff --git a/tests/regression/issue_249/SConscript b/tests/regression/issue_249/SConscript new file mode 100644 index 0000000..ba66712 --- /dev/null +++ b/tests/regression/issue_249/SConscript @@ -0,0 +1,12 @@ +# Regression test for Issue 249: proto3 mode pb_decode() corrupts callback fields +Import('env') + +env.NanopbProto('test') + +p = env.Program(["test.c", + "test.pb.c", + "$COMMON/pb_decode.o", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) +env.RunTest(p) + diff --git a/tests/regression/issue_249/test.c b/tests/regression/issue_249/test.c new file mode 100644 index 0000000..a37180f --- /dev/null +++ b/tests/regression/issue_249/test.c @@ -0,0 +1,59 @@ +#include "test.pb.h" +#include <unittests.h> +#include <pb_encode.h> +#include <pb_decode.h> + +static bool write_array(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + int i; + for (i = 0; i < 5; i++) + { + if (!pb_encode_tag_for_field(stream, field)) + return false; + if (!pb_encode_varint(stream, 1000 + i)) + return false; + } + + return true; +} + +static bool read_array(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + uint32_t i; + int *sum = *arg; + + if (!pb_decode_varint32(stream, &i)) + return false; + + *sum += i; + + return true; +} + +int main() +{ + int status = 0; + pb_byte_t buf[128] = {0}; + pb_size_t msglen; + + { + MainMessage msg = MainMessage_init_zero; + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + msg.submsg.foo.funcs.encode = &write_array; + TEST(pb_encode(&stream, MainMessage_fields, &msg)); + msglen = stream.bytes_written; + } + + { + MainMessage msg = MainMessage_init_zero; + pb_istream_t stream = pb_istream_from_buffer(buf, msglen); + int sum = 0; + msg.submsg.foo.funcs.decode = &read_array; + msg.submsg.foo.arg = ∑ + TEST(pb_decode(&stream, MainMessage_fields, &msg)); + TEST(sum == 1000 + 1001 + 1002 + 1003 + 1004); + } + + return status; +} + diff --git a/tests/regression/issue_249/test.proto b/tests/regression/issue_249/test.proto new file mode 100644 index 0000000..eaa2abd --- /dev/null +++ b/tests/regression/issue_249/test.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +message SubMessage { + repeated int32 foo = 1; +} + +message MainMessage { + SubMessage submsg = 1; +} + diff --git a/tests/regression/issue_253/SConscript b/tests/regression/issue_253/SConscript new file mode 100644 index 0000000..5a16948 --- /dev/null +++ b/tests/regression/issue_253/SConscript @@ -0,0 +1,15 @@ +# Regression test for Issue 253: Wrong calculated message maximum size + +Import('env') + +env.NanopbProto('short_array') + +p = env.Program(['short_array.c', + 'short_array.pb.c', + "$COMMON/pb_decode.o", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(p) + + diff --git a/tests/regression/issue_253/short_array.c b/tests/regression/issue_253/short_array.c new file mode 100644 index 0000000..5ed6c3f --- /dev/null +++ b/tests/regression/issue_253/short_array.c @@ -0,0 +1,24 @@ +#include <unittests.h> +#include <pb_encode.h> +#include "short_array.pb.h" + +int main() +{ + int status = 0; + + COMMENT("Test message length calculation for short arrays"); + { + uint8_t buffer[TestMessage_size] = {0}; + pb_ostream_t ostream = pb_ostream_from_buffer(buffer, TestMessage_size); + TestMessage msg = TestMessage_init_zero; + + msg.rep_uint32_count = 1; + msg.rep_uint32[0] = (1 << 31); + + TEST(pb_encode(&ostream, TestMessage_fields, &msg)); + TEST(ostream.bytes_written == TestMessage_size); + } + + return status; +} + diff --git a/tests/regression/issue_253/short_array.proto b/tests/regression/issue_253/short_array.proto new file mode 100644 index 0000000..5a5d8a3 --- /dev/null +++ b/tests/regression/issue_253/short_array.proto @@ -0,0 +1,7 @@ +syntax = "proto2"; +import "nanopb.proto"; + +message TestMessage { + repeated uint32 rep_uint32 = 1 [(nanopb).max_count = 1]; +} + diff --git a/tests/regression/issue_256/SConscript b/tests/regression/issue_256/SConscript new file mode 100644 index 0000000..b2c3e86 --- /dev/null +++ b/tests/regression/issue_256/SConscript @@ -0,0 +1,16 @@ +# Regression test for Issue 256: Proto3 mode skips submessages even when +# later array fields have non-zero value + +Import('env') + +env.NanopbProto('submsg_array') + +p = env.Program(['submsg_array.c', + 'submsg_array.pb.c', + "$COMMON/pb_decode.o", + "$COMMON/pb_encode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(p) + + diff --git a/tests/regression/issue_256/submsg_array.c b/tests/regression/issue_256/submsg_array.c new file mode 100644 index 0000000..c63bd30 --- /dev/null +++ b/tests/regression/issue_256/submsg_array.c @@ -0,0 +1,38 @@ +#include <unittests.h> +#include <pb_encode.h> +#include <pb_decode.h> +#include "submsg_array.pb.h" + +int main() +{ + int status = 0; + + COMMENT("Test encoding for submessage with array"); + { + uint8_t buffer[TestMessage_size] = {0}; + pb_ostream_t ostream = pb_ostream_from_buffer(buffer, TestMessage_size); + TestMessage msg = TestMessage_init_zero; + + msg.submsg.rep_uint32_count = 3; + msg.submsg.rep_uint32[0] = 0; + msg.submsg.rep_uint32[1] = 1; + msg.submsg.rep_uint32[2] = 2; + + TEST(pb_encode(&ostream, TestMessage_fields, &msg)); + TEST(ostream.bytes_written > 0); + + { + pb_istream_t istream = pb_istream_from_buffer(buffer, ostream.bytes_written); + TestMessage msg2 = TestMessage_init_zero; + + TEST(pb_decode(&istream, TestMessage_fields, &msg2)); + TEST(msg2.submsg.rep_uint32_count == 3); + TEST(msg2.submsg.rep_uint32[0] == 0); + TEST(msg2.submsg.rep_uint32[1] == 1); + TEST(msg2.submsg.rep_uint32[2] == 2); + } + } + + return status; +} + diff --git a/tests/regression/issue_256/submsg_array.proto b/tests/regression/issue_256/submsg_array.proto new file mode 100644 index 0000000..4964a05 --- /dev/null +++ b/tests/regression/issue_256/submsg_array.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +import "nanopb.proto"; + +message SubMessage { + repeated uint32 rep_uint32 = 1 [(nanopb).max_count = 3]; +} + +message TestMessage { + SubMessage submsg = 1; +} + diff --git a/tests/regression/issue_259/SConscript b/tests/regression/issue_259/SConscript new file mode 100644 index 0000000..8340d45 --- /dev/null +++ b/tests/regression/issue_259/SConscript @@ -0,0 +1,22 @@ +# Check that callback fields inside malloc()ed messages +# are correctly initialized. + +Import('env', 'malloc_env') + +env.NanopbProto('callback_pointer') + +p = malloc_env.Program(["callback_pointer.c", + "callback_pointer.pb.c", + "$COMMON/pb_decode_with_malloc.o", + "$COMMON/pb_common_with_malloc.o", + "$COMMON/malloc_wrappers.o"]) + +# Run test under valgrind if available +valgrind = env.WhereIs('valgrind') +kwargs = {} +if valgrind: + kwargs['COMMAND'] = valgrind + kwargs['ARGS'] = ["-q", "--error-exitcode=99", p[0].abspath] + +env.RunTest(p, **kwargs) + diff --git a/tests/regression/issue_259/callback_pointer.c b/tests/regression/issue_259/callback_pointer.c new file mode 100644 index 0000000..f150fbe --- /dev/null +++ b/tests/regression/issue_259/callback_pointer.c @@ -0,0 +1,30 @@ +#include "callback_pointer.pb.h" +#include <unittests.h> +#include <pb_decode.h> + +int main() +{ + int status = 0; + const uint8_t msgdata[] = {0x0A, 0x02, 0x08, 0x7F}; + + MainMessage msg = MainMessage_init_zero; + + { + pb_istream_t stream = pb_istream_from_buffer(msgdata, sizeof(msgdata)); + COMMENT("Running first decode"); + TEST(pb_decode(&stream, MainMessage_fields, &msg)); + pb_release(MainMessage_fields, &msg); + } + + { + pb_istream_t stream = pb_istream_from_buffer(msgdata, sizeof(msgdata)); + COMMENT("Running second decode"); + TEST(pb_decode(&stream, MainMessage_fields, &msg)); + pb_release(MainMessage_fields, &msg); + } + + TEST(get_alloc_count() == 0); + + return status; +} + diff --git a/tests/regression/issue_259/callback_pointer.proto b/tests/regression/issue_259/callback_pointer.proto new file mode 100644 index 0000000..a2d04e4 --- /dev/null +++ b/tests/regression/issue_259/callback_pointer.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; +import 'nanopb.proto'; + +message SubMessage { + optional int32 foo = 1 [(nanopb).type = FT_CALLBACK]; +} + +message MainMessage { + optional SubMessage bar = 1 [(nanopb).type = FT_POINTER]; +} + diff --git a/tests/regression/issue_306/SConscript b/tests/regression/issue_306/SConscript new file mode 100644 index 0000000..d3badec --- /dev/null +++ b/tests/regression/issue_306/SConscript @@ -0,0 +1,7 @@ +# Check that generator gives a warning about large extension field number. + +Import('env') + +env.NanopbProto('large_extension') + +env.Match(['large_extension.pb.c', 'large_extension.expected']) diff --git a/tests/regression/issue_306/large_extension.expected b/tests/regression/issue_306/large_extension.expected new file mode 100644 index 0000000..a90bc32 --- /dev/null +++ b/tests/regression/issue_306/large_extension.expected @@ -0,0 +1 @@ +PB_FIELD_32BIT diff --git a/tests/regression/issue_306/large_extension.proto b/tests/regression/issue_306/large_extension.proto new file mode 100644 index 0000000..c3c8f6f --- /dev/null +++ b/tests/regression/issue_306/large_extension.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +message Foo { + extensions 1 to max; +} + +extend Foo { + optional int32 foo_ext = 99999; +} + diff --git a/tests/regression/issue_322/SConscript b/tests/regression/issue_322/SConscript new file mode 100644 index 0000000..ec08e74 --- /dev/null +++ b/tests/regression/issue_322/SConscript @@ -0,0 +1,14 @@ +# Check that default values with special characters are +# correctly handled. + +Import('env') + +env.NanopbProto('defaults') + +p = env.Program(["defaults.c", + "defaults.pb.c", + "$COMMON/pb_decode.o", + "$COMMON/pb_common.o"]) + +env.RunTest(p) + diff --git a/tests/regression/issue_322/defaults.c b/tests/regression/issue_322/defaults.c new file mode 100644 index 0000000..b285754 --- /dev/null +++ b/tests/regression/issue_322/defaults.c @@ -0,0 +1,44 @@ +#include "defaults.pb.h" +#include <unittests.h> +#include <pb_decode.h> + +int check_defaults(const DefaultsMsg *msg) +{ + int status = 0; + + TEST(msg->b1[0] == 0xDE && msg->b1[1] == 0xAD && msg->b1[2] == 0x00 && + msg->b1[3] == 0xBE && msg->b1[4] == 0xEF); + TEST(msg->b2.bytes[0] == 0xDE && msg->b2.bytes[1] == 0xAD && + msg->b2.bytes[2] == 0x00 && msg->b2.bytes[3] == 0xBE && + msg->b2.bytes[4] == 0xEF && msg->b2.size == 5); + TEST(msg->b3.bytes[0] == 0xDE && msg->b3.bytes[1] == 0xAD && + msg->b3.bytes[2] == 0x00 && msg->b3.bytes[3] == 0xBE && + msg->b3.bytes[4] == 0xEF && msg->b2.size == 5); + TEST(msg->s1[0] == (char)0xC3 && msg->s1[1] == (char)0xA4 && + msg->s1[2] == (char)0xC3 && msg->s1[3] == (char)0xB6 && + msg->s1[4] == '\0'); + + return status; +} + +int main() +{ + int status = 0; + + { + DefaultsMsg msg = DefaultsMsg_init_default; + COMMENT("Checking defaults from static initializer"); + status += check_defaults(&msg); + } + + { + DefaultsMsg msg = DefaultsMsg_init_zero; + pb_istream_t empty = {0,0,0}; + pb_decode(&empty, DefaultsMsg_fields, &msg); + COMMENT("Checking defaults set at runtime"); + status += check_defaults(&msg); + } + + return status; +} + diff --git a/tests/regression/issue_322/defaults.proto b/tests/regression/issue_322/defaults.proto new file mode 100644 index 0000000..359344d --- /dev/null +++ b/tests/regression/issue_322/defaults.proto @@ -0,0 +1,11 @@ +syntax = "proto2"; +import 'nanopb.proto'; + +message DefaultsMsg { + optional bytes b1 = 1 [default = "\xDE\xAD\x00\xBE\xEF", (nanopb).max_size = 5, (nanopb).fixed_length=true]; + optional bytes b2 = 2 [default = "\xDE\xAD\x00\xBE\xEF", (nanopb).max_size = 5]; + optional bytes b3 = 3 [default = "\xDE\xAD\000\xBE\xEF", (nanopb).max_size = 15]; + optional string s1 = 4 [default = "รครถ", (nanopb).max_size = 8]; +} + + diff --git a/tests/site_scons/site_init.py b/tests/site_scons/site_init.py index 5fb06d6..56e227b 100644 --- a/tests/site_scons/site_init.py +++ b/tests/site_scons/site_init.py @@ -27,16 +27,16 @@ def add_nanopb_builders(env): if env.has_key('ARGS'): args.extend(env['ARGS']) - print 'Command line: ' + str(args) + print('Command line: ' + str(args)) pipe = subprocess.Popen(args, stdin = infile, stdout = open(str(target[0]), 'w'), stderr = sys.stderr) result = pipe.wait() if result == 0: - print '\033[32m[ OK ]\033[0m Ran ' + args[0] + print('\033[32m[ OK ]\033[0m Ran ' + args[0]) else: - print '\033[31m[FAIL]\033[0m Program ' + args[0] + ' returned ' + str(result) + print('\033[31m[FAIL]\033[0m Program ' + args[0] + ' returned ' + str(result)) return result run_test_builder = Builder(action = run_test, @@ -70,10 +70,10 @@ def add_nanopb_builders(env): data1 = open(str(source[0]), 'rb').read() data2 = open(str(source[1]), 'rb').read() if data1 == data2: - print '\033[32m[ OK ]\033[0m Files equal: ' + str(source[0]) + ' and ' + str(source[1]) + print('\033[32m[ OK ]\033[0m Files equal: ' + str(source[0]) + ' and ' + str(source[1])) return 0 else: - print '\033[31m[FAIL]\033[0m Files differ: ' + str(source[0]) + ' and ' + str(source[1]) + print('\033[31m[FAIL]\033[0m Files differ: ' + str(source[0]) + ' and ' + str(source[1])) return 1 compare_builder = Builder(action = compare_files, @@ -85,11 +85,22 @@ def add_nanopb_builders(env): data = open(str(source[0]), 'rU').read() patterns = open(str(source[1])) for pattern in patterns: - if pattern.strip() and not re.search(pattern.strip(), data, re.MULTILINE): - print '\033[31m[FAIL]\033[0m Pattern not found in ' + str(source[0]) + ': ' + pattern - return 1 + if pattern.strip(): + invert = False + if pattern.startswith('! '): + invert = True + pattern = pattern[2:] + + status = re.search(pattern.strip(), data, re.MULTILINE) + + if not status and not invert: + print('\033[31m[FAIL]\033[0m Pattern not found in ' + str(source[0]) + ': ' + pattern) + return 1 + elif status and invert: + print('\033[31m[FAIL]\033[0m Pattern should not exist, but does in ' + str(source[0]) + ': ' + pattern) + return 1 else: - print '\033[32m[ OK ]\033[0m All patterns found in ' + str(source[0]) + print('\033[32m[ OK ]\033[0m All patterns found in ' + str(source[0])) return 0 match_builder = Builder(action = match_files, suffix = '.matched') diff --git a/tests/site_scons/site_tools/nanopb.py b/tests/site_scons/site_tools/nanopb.py index b3e58fa..c72a45d 100644 --- a/tests/site_scons/site_tools/nanopb.py +++ b/tests/site_scons/site_tools/nanopb.py @@ -118,7 +118,7 @@ def generate(env): env.SetDefault(PROTOCPATH = ['.', os.path.join(env['NANOPB'], 'generator', 'proto')]) - env.SetDefault(NANOPB_PROTO_CMD = '$PROTOC $PROTOC_OPTS --nanopb_out=. $SOURCE') + env.SetDefault(NANOPB_PROTO_CMD = '$PROTOC $PROTOCFLAGS --nanopb_out=. $SOURCES') env['BUILDERS']['NanopbProto'] = _nanopb_proto_builder def exists(env): diff --git a/tests/special_characters/funny-proto+name has.characters.proto b/tests/special_characters/funny-proto+name has.characters.proto index e69de29..26b2cb1 100644 --- a/tests/special_characters/funny-proto+name has.characters.proto +++ b/tests/special_characters/funny-proto+name has.characters.proto @@ -0,0 +1 @@ +syntax="proto2"; diff --git a/tests/splint/SConscript b/tests/splint/SConscript index c1432dd..cd4b5b9 100644 --- a/tests/splint/SConscript +++ b/tests/splint/SConscript @@ -11,3 +11,6 @@ if p: env.Command('pb_encode.splint', '$NANOPB/pb_encode.c', 'splint -f splint/splint.rc $SOURCE 2> $TARGET') + env.Command('pb_common.splint', '$NANOPB/pb_common.c', + 'splint -f splint/splint.rc $SOURCE 2> $TARGET') + diff --git a/tests/splint/splint.rc b/tests/splint/splint.rc index 421f567..e47d3c2 100644 --- a/tests/splint/splint.rc +++ b/tests/splint/splint.rc @@ -2,7 +2,6 @@ +partial +matchanyintegral +strictlib --isoreserved # to be fixed in 0.3 -nullassign -predboolint -predboolptr @@ -28,6 +27,7 @@ -kepttrans -branchstate -immediatetrans +-mustfreefresh # These tests give false positives, compiler typically has # better warnings for these. diff --git a/tests/without_64bit/SConscript b/tests/without_64bit/SConscript new file mode 100644 index 0000000..be415d8 --- /dev/null +++ b/tests/without_64bit/SConscript @@ -0,0 +1,49 @@ +# Run the alltypes test case, but compile with PB_WITHOUT_64BIT. + +Import("env") + +env.NanopbProto(["alltypes", "alltypes.options"]) + +# Define the compilation options +opts = env.Clone() +opts.Append(CPPDEFINES = {'PB_WITHOUT_64BIT': 1, 'HAVE_STDINT_H': 0, 'PB_SYSTEM_HEADER': '\\"no_64bit_syshdr.h\\"'}) +opts.Append(CPPPATH = "#without_64bit") + +if 'SYSHDR' in opts: + opts.Append(CPPDEFINES = {'PB_OLD_SYSHDR': opts['SYSHDR']}) + +# Build new version of core +strict = opts.Clone() +strict.Append(CFLAGS = strict['CORECFLAGS']) +strict.Object("pb_decode_no64bit.o", "$NANOPB/pb_decode.c") +strict.Object("pb_encode_no64bit.o", "$NANOPB/pb_encode.c") +strict.Object("pb_common_no64bit.o", "$NANOPB/pb_common.c") + +# Now build and run the test normally. +enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode_no64bit.o", "pb_common_no64bit.o"]) +dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode_no64bit.o", "pb_common_no64bit.o"]) + +env.RunTest(enc) +env.RunTest([dec, "encode_alltypes.output"]) + +# Re-encode the data using protoc, and check that the results from nanopb +# match byte-per-byte to the protoc output. +env.Decode("encode_alltypes.output.decoded", + ["encode_alltypes.output", "alltypes.proto"], + MESSAGE='AllTypes') +env.Encode("encode_alltypes.output.recoded", + ["encode_alltypes.output.decoded", "alltypes.proto"], + MESSAGE='AllTypes') +env.Compare(["encode_alltypes.output", "encode_alltypes.output.recoded"]) + + +# Do the same checks with the optional fields present. +env.RunTest("optionals.output", enc, ARGS = ['1']) +env.RunTest("optionals.decout", [dec, "optionals.output"], ARGS = ['1']) +env.Decode("optionals.output.decoded", + ["optionals.output", "alltypes.proto"], + MESSAGE='AllTypes') +env.Encode("optionals.output.recoded", + ["optionals.output.decoded", "alltypes.proto"], + MESSAGE='AllTypes') +env.Compare(["optionals.output", "optionals.output.recoded"]) diff --git a/tests/without_64bit/alltypes.options b/tests/without_64bit/alltypes.options new file mode 100644 index 0000000..0d5ab12 --- /dev/null +++ b/tests/without_64bit/alltypes.options @@ -0,0 +1,3 @@ +* max_size:16 +* max_count:5 +*.*fbytes fixed_length:true max_size:4 diff --git a/tests/without_64bit/alltypes.proto b/tests/without_64bit/alltypes.proto new file mode 100644 index 0000000..240685b --- /dev/null +++ b/tests/without_64bit/alltypes.proto @@ -0,0 +1,100 @@ +syntax = "proto2"; +// package name placeholder + +message SubMessage { + required string substuff1 = 1 [default = "1"]; + required int32 substuff2 = 2 [default = 2]; + optional fixed32 substuff3 = 3 [default = 3]; +} + +message EmptyMessage { + +} + +enum HugeEnum { + Negative = -2147483647; /* protoc doesn't accept -2147483648 here */ + Positive = 2147483647; +} + +message Limits { + required int32 int32_min = 1 [default = 2147483647]; + required int32 int32_max = 2 [default = -2147483647]; + required uint32 uint32_min = 3 [default = 4294967295]; + required uint32 uint32_max = 4 [default = 0]; + required HugeEnum enum_min = 9 [default = Positive]; + required HugeEnum enum_max = 10 [default = Negative]; +} + +enum MyEnum { + Zero = 0; + First = 1; + Second = 2; + Truth = 42; +} + +message AllTypes { + required int32 req_int32 = 1; + required uint32 req_uint32 = 3; + required sint32 req_sint32 = 5; + required bool req_bool = 7; + + required fixed32 req_fixed32 = 8; + required sfixed32 req_sfixed32= 9; + required float req_float = 10; + + required string req_string = 14; + required bytes req_bytes = 15; + required SubMessage req_submsg = 16; + required MyEnum req_enum = 17; + required EmptyMessage req_emptymsg = 18; + required bytes req_fbytes = 19; + + repeated int32 rep_int32 = 21 [packed = true]; + repeated uint32 rep_uint32 = 23 [packed = true]; + repeated sint32 rep_sint32 = 25 [packed = true]; + repeated bool rep_bool = 27 [packed = true]; + + repeated fixed32 rep_fixed32 = 28 [packed = true]; + repeated sfixed32 rep_sfixed32= 29 [packed = true]; + repeated float rep_float = 30 [packed = true]; + + repeated string rep_string = 34; + repeated bytes rep_bytes = 35; + repeated SubMessage rep_submsg = 36; + repeated MyEnum rep_enum = 37 [packed = true]; + repeated EmptyMessage rep_emptymsg = 38; + repeated bytes rep_fbytes = 39; + + optional int32 opt_int32 = 41 [default = 4041]; + optional uint32 opt_uint32 = 43 [default = 4043]; + optional sint32 opt_sint32 = 45 [default = 4045]; + optional bool opt_bool = 47 [default = false]; + + optional fixed32 opt_fixed32 = 48 [default = 4048]; + optional sfixed32 opt_sfixed32= 49 [default = 4049]; + optional float opt_float = 50 [default = 4050]; + + optional string opt_string = 54 [default = "4054"]; + optional bytes opt_bytes = 55 [default = "4055"]; + optional SubMessage opt_submsg = 56; + optional MyEnum opt_enum = 57 [default = Second]; + optional EmptyMessage opt_emptymsg = 58; + optional bytes opt_fbytes = 59 [default = "4059"]; + + oneof oneof + { + SubMessage oneof_msg1 = 60; + EmptyMessage oneof_msg2 = 61; + } + + // Check that extreme integer values are handled correctly + required Limits req_limits = 98; + + // Just to make sure that the size of the fields has been calculated + // properly, i.e. otherwise a bug in last field might not be detected. + required int32 end = 99; + + + extensions 200 to 255; +} + diff --git a/tests/without_64bit/decode_alltypes.c b/tests/without_64bit/decode_alltypes.c new file mode 100644 index 0000000..6b5ff8e --- /dev/null +++ b/tests/without_64bit/decode_alltypes.c @@ -0,0 +1,185 @@ +/* Tests the decoding of all types. + * This is the counterpart of test_encode3. + * Run e.g. ./test_encode3 | ./test_decode3 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <pb_decode.h> +#include "alltypes.pb.h" +#include "test_helpers.h" + +#define TEST(x) if (!(x)) { \ + printf("Test " #x " failed.\n"); \ + return false; \ + } + +/* This function is called once from main(), it handles + the decoding and checks the fields. */ +bool check_alltypes(pb_istream_t *stream, int mode) +{ + /* Uses _init_default to just make sure that it works. */ + AllTypes alltypes = AllTypes_init_default; + + /* Fill with garbage to better detect initialization errors */ + memset(&alltypes, 0xAA, sizeof(alltypes)); + alltypes.extensions = 0; + + if (!pb_decode(stream, AllTypes_fields, &alltypes)) + return false; + + TEST(alltypes.req_int32 == -1001); + TEST(alltypes.req_uint32 == 1003); + TEST(alltypes.req_sint32 == -1005); + TEST(alltypes.req_bool == true); + + TEST(alltypes.req_fixed32 == 1008); + TEST(alltypes.req_sfixed32 == -1009); + TEST(alltypes.req_float == 1010.0f); + + TEST(strcmp(alltypes.req_string, "1014") == 0); + TEST(alltypes.req_bytes.size == 4); + TEST(memcmp(alltypes.req_bytes.bytes, "1015", 4) == 0); + TEST(strcmp(alltypes.req_submsg.substuff1, "1016") == 0); + TEST(alltypes.req_submsg.substuff2 == 1016); + TEST(alltypes.req_submsg.substuff3 == 3); + TEST(alltypes.req_enum == MyEnum_Truth); + TEST(memcmp(alltypes.req_fbytes, "1019", 4) == 0); + + TEST(alltypes.rep_int32_count == 5 && alltypes.rep_int32[4] == -2001 && alltypes.rep_int32[0] == 0); + TEST(alltypes.rep_uint32_count == 5 && alltypes.rep_uint32[4] == 2003 && alltypes.rep_uint32[0] == 0); + TEST(alltypes.rep_sint32_count == 5 && alltypes.rep_sint32[4] == -2005 && alltypes.rep_sint32[0] == 0); + TEST(alltypes.rep_bool_count == 5 && alltypes.rep_bool[4] == true && alltypes.rep_bool[0] == false); + + TEST(alltypes.rep_fixed32_count == 5 && alltypes.rep_fixed32[4] == 2008 && alltypes.rep_fixed32[0] == 0); + TEST(alltypes.rep_sfixed32_count == 5 && alltypes.rep_sfixed32[4] == -2009 && alltypes.rep_sfixed32[0] == 0); + TEST(alltypes.rep_float_count == 5 && alltypes.rep_float[4] == 2010.0f && alltypes.rep_float[0] == 0.0f); + + TEST(alltypes.rep_string_count == 5 && strcmp(alltypes.rep_string[4], "2014") == 0 && alltypes.rep_string[0][0] == '\0'); + TEST(alltypes.rep_bytes_count == 5 && alltypes.rep_bytes[4].size == 4 && alltypes.rep_bytes[0].size == 0); + TEST(memcmp(alltypes.rep_bytes[4].bytes, "2015", 4) == 0); + + TEST(alltypes.rep_submsg_count == 5); + TEST(strcmp(alltypes.rep_submsg[4].substuff1, "2016") == 0 && alltypes.rep_submsg[0].substuff1[0] == '\0'); + TEST(alltypes.rep_submsg[4].substuff2 == 2016 && alltypes.rep_submsg[0].substuff2 == 0); + TEST(alltypes.rep_submsg[4].substuff3 == 2016 && alltypes.rep_submsg[0].substuff3 == 3); + + TEST(alltypes.rep_enum_count == 5 && alltypes.rep_enum[4] == MyEnum_Truth && alltypes.rep_enum[0] == MyEnum_Zero); + TEST(alltypes.rep_emptymsg_count == 5); + TEST(alltypes.rep_fbytes_count == 5); + TEST(alltypes.rep_fbytes[0][0] == 0 && alltypes.rep_fbytes[0][3] == 0); + TEST(memcmp(alltypes.rep_fbytes[4], "2019", 4) == 0); + + if (mode == 0) + { + /* Expect default values */ + TEST(alltypes.has_opt_int32 == false); + TEST(alltypes.opt_int32 == 4041); + TEST(alltypes.has_opt_uint32 == false); + TEST(alltypes.opt_uint32 == 4043); + TEST(alltypes.has_opt_sint32 == false); + TEST(alltypes.opt_sint32 == 4045); + TEST(alltypes.has_opt_bool == false); + TEST(alltypes.opt_bool == false); + + TEST(alltypes.has_opt_fixed32 == false); + TEST(alltypes.opt_fixed32 == 4048); + TEST(alltypes.has_opt_sfixed32 == false); + TEST(alltypes.opt_sfixed32 == 4049); + TEST(alltypes.has_opt_float == false); + TEST(alltypes.opt_float == 4050.0f); + + TEST(alltypes.has_opt_string == false); + TEST(strcmp(alltypes.opt_string, "4054") == 0); + TEST(alltypes.has_opt_bytes == false); + TEST(alltypes.opt_bytes.size == 4); + TEST(memcmp(alltypes.opt_bytes.bytes, "4055", 4) == 0); + TEST(alltypes.has_opt_submsg == false); + TEST(strcmp(alltypes.opt_submsg.substuff1, "1") == 0); + TEST(alltypes.opt_submsg.substuff2 == 2); + TEST(alltypes.opt_submsg.substuff3 == 3); + TEST(alltypes.has_opt_enum == false); + TEST(alltypes.opt_enum == MyEnum_Second); + TEST(alltypes.has_opt_emptymsg == false); + TEST(alltypes.has_opt_fbytes == false); + TEST(memcmp(alltypes.opt_fbytes, "4059", 4) == 0); + + TEST(alltypes.which_oneof == 0); + } + else + { + /* Expect filled-in values */ + TEST(alltypes.has_opt_int32 == true); + TEST(alltypes.opt_int32 == 3041); + TEST(alltypes.has_opt_uint32 == true); + TEST(alltypes.opt_uint32 == 3043); + TEST(alltypes.has_opt_sint32 == true); + TEST(alltypes.opt_sint32 == 3045); + TEST(alltypes.has_opt_bool == true); + TEST(alltypes.opt_bool == true); + + TEST(alltypes.has_opt_fixed32 == true); + TEST(alltypes.opt_fixed32 == 3048); + TEST(alltypes.has_opt_sfixed32 == true); + TEST(alltypes.opt_sfixed32 == 3049); + TEST(alltypes.has_opt_float == true); + TEST(alltypes.opt_float == 3050.0f); + + TEST(alltypes.has_opt_string == true); + TEST(strcmp(alltypes.opt_string, "3054") == 0); + TEST(alltypes.has_opt_bytes == true); + TEST(alltypes.opt_bytes.size == 4); + TEST(memcmp(alltypes.opt_bytes.bytes, "3055", 4) == 0); + TEST(alltypes.has_opt_submsg == true); + TEST(strcmp(alltypes.opt_submsg.substuff1, "3056") == 0); + TEST(alltypes.opt_submsg.substuff2 == 3056); + TEST(alltypes.opt_submsg.substuff3 == 3); + TEST(alltypes.has_opt_enum == true); + TEST(alltypes.opt_enum == MyEnum_Truth); + TEST(alltypes.has_opt_emptymsg == true); + TEST(alltypes.has_opt_fbytes == true); + TEST(memcmp(alltypes.opt_fbytes, "3059", 4) == 0); + + TEST(alltypes.which_oneof == AllTypes_oneof_msg1_tag); + TEST(strcmp(alltypes.oneof.oneof_msg1.substuff1, "4059") == 0); + TEST(alltypes.oneof.oneof_msg1.substuff2 == 4059); + } + + TEST(alltypes.req_limits.int32_min == INT32_MIN); + TEST(alltypes.req_limits.int32_max == INT32_MAX); + TEST(alltypes.req_limits.uint32_min == 0); + TEST(alltypes.req_limits.uint32_max == UINT32_MAX); + TEST(alltypes.req_limits.enum_min == HugeEnum_Negative); + TEST(alltypes.req_limits.enum_max == HugeEnum_Positive); + + TEST(alltypes.end == 1099); + + return true; +} + +int main(int argc, char **argv) +{ + uint8_t buffer[1024]; + size_t count; + pb_istream_t stream; + + /* Whether to expect the optional values or the default values. */ + int mode = (argc > 1) ? atoi(argv[1]) : 0; + + /* Read the data into buffer */ + SET_BINARY_MODE(stdin); + count = fread(buffer, 1, sizeof(buffer), stdin); + + /* Construct a pb_istream_t for reading from the buffer */ + stream = pb_istream_from_buffer(buffer, count); + + /* Decode and print out the stuff */ + if (!check_alltypes(&stream, mode)) + { + printf("Parsing failed: %s\n", PB_GET_ERROR(&stream)); + return 1; + } else { + return 0; + } +} diff --git a/tests/without_64bit/encode_alltypes.c b/tests/without_64bit/encode_alltypes.c new file mode 100644 index 0000000..9fe26f6 --- /dev/null +++ b/tests/without_64bit/encode_alltypes.c @@ -0,0 +1,124 @@ +/* Attempts to test all the datatypes supported by ProtoBuf. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pb_encode.h> +#include "alltypes.pb.h" +#include "test_helpers.h" + +int main(int argc, char **argv) +{ + int mode = (argc > 1) ? atoi(argv[1]) : 0; + + /* Initialize the structure with constants */ + AllTypes alltypes = AllTypes_init_zero; + + alltypes.req_int32 = -1001; + alltypes.req_uint32 = 1003; + alltypes.req_sint32 = -1005; + alltypes.req_bool = true; + + alltypes.req_fixed32 = 1008; + alltypes.req_sfixed32 = -1009; + alltypes.req_float = 1010.0f; + + strcpy(alltypes.req_string, "1014"); + alltypes.req_bytes.size = 4; + memcpy(alltypes.req_bytes.bytes, "1015", 4); + strcpy(alltypes.req_submsg.substuff1, "1016"); + alltypes.req_submsg.substuff2 = 1016; + alltypes.req_enum = MyEnum_Truth; + memcpy(alltypes.req_fbytes, "1019", 4); + + alltypes.rep_int32_count = 5; alltypes.rep_int32[4] = -2001; + alltypes.rep_uint32_count = 5; alltypes.rep_uint32[4] = 2003; + alltypes.rep_sint32_count = 5; alltypes.rep_sint32[4] = -2005; + alltypes.rep_bool_count = 5; alltypes.rep_bool[4] = true; + + alltypes.rep_fixed32_count = 5; alltypes.rep_fixed32[4] = 2008; + alltypes.rep_sfixed32_count = 5; alltypes.rep_sfixed32[4] = -2009; + alltypes.rep_float_count = 5; alltypes.rep_float[4] = 2010.0f; + + alltypes.rep_string_count = 5; strcpy(alltypes.rep_string[4], "2014"); + alltypes.rep_bytes_count = 5; alltypes.rep_bytes[4].size = 4; + memcpy(alltypes.rep_bytes[4].bytes, "2015", 4); + + alltypes.rep_submsg_count = 5; + strcpy(alltypes.rep_submsg[4].substuff1, "2016"); + alltypes.rep_submsg[4].substuff2 = 2016; + alltypes.rep_submsg[4].has_substuff3 = true; + alltypes.rep_submsg[4].substuff3 = 2016; + + alltypes.rep_enum_count = 5; alltypes.rep_enum[4] = MyEnum_Truth; + alltypes.rep_emptymsg_count = 5; + + alltypes.rep_fbytes_count = 5; + memcpy(alltypes.rep_fbytes[4], "2019", 4); + + alltypes.req_limits.int32_min = INT32_MIN; + alltypes.req_limits.int32_max = INT32_MAX; + alltypes.req_limits.uint32_min = 0; + alltypes.req_limits.uint32_max = UINT32_MAX; + alltypes.req_limits.enum_min = HugeEnum_Negative; + alltypes.req_limits.enum_max = HugeEnum_Positive; + + if (mode != 0) + { + /* Fill in values for optional fields */ + alltypes.has_opt_int32 = true; + alltypes.opt_int32 = 3041; + alltypes.has_opt_uint32 = true; + alltypes.opt_uint32 = 3043; + alltypes.has_opt_sint32 = true; + alltypes.opt_sint32 = 3045; + alltypes.has_opt_bool = true; + alltypes.opt_bool = true; + + alltypes.has_opt_fixed32 = true; + alltypes.opt_fixed32 = 3048; + alltypes.has_opt_sfixed32 = true; + alltypes.opt_sfixed32 = 3049; + alltypes.has_opt_float = true; + alltypes.opt_float = 3050.0f; + + alltypes.has_opt_string = true; + strcpy(alltypes.opt_string, "3054"); + alltypes.has_opt_bytes = true; + alltypes.opt_bytes.size = 4; + memcpy(alltypes.opt_bytes.bytes, "3055", 4); + alltypes.has_opt_submsg = true; + strcpy(alltypes.opt_submsg.substuff1, "3056"); + alltypes.opt_submsg.substuff2 = 3056; + alltypes.has_opt_enum = true; + alltypes.opt_enum = MyEnum_Truth; + alltypes.has_opt_emptymsg = true; + alltypes.has_opt_fbytes = true; + memcpy(alltypes.opt_fbytes, "3059", 4); + + alltypes.which_oneof = AllTypes_oneof_msg1_tag; + strcpy(alltypes.oneof.oneof_msg1.substuff1, "4059"); + alltypes.oneof.oneof_msg1.substuff2 = 4059; + } + + alltypes.end = 1099; + + { + uint8_t buffer[AllTypes_size]; + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + + /* Now encode it and check if we succeeded. */ + if (pb_encode(&stream, AllTypes_fields, &alltypes)) + { + SET_BINARY_MODE(stdout); + fwrite(buffer, 1, stream.bytes_written, stdout); + return 0; /* Success */ + } + else + { + fprintf(stderr, "Encoding failed: %s\n", PB_GET_ERROR(&stream)); + return 1; /* Failure */ + } + } +} diff --git a/tests/without_64bit/no_64bit_syshdr.h b/tests/without_64bit/no_64bit_syshdr.h new file mode 100644 index 0000000..970178f --- /dev/null +++ b/tests/without_64bit/no_64bit_syshdr.h @@ -0,0 +1,14 @@ +/* This wrapper undefines (u)int64_t */ + +#ifdef PB_OLD_SYSHDR +#include PB_OLD_SYSHDR +#else +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> +#include <string.h> +#endif + +#define uint64_t disabled_uint64_t +#define int64_t disabled_int64_t + |