Add testcase for anonymous unions + few fixes.
authorPetteri Aimonen <jpa@git.mail.kapsi.fi>
Sat, 14 Nov 2015 20:23:48 +0000 (22:23 +0200)
committerPetteri Aimonen <jpa@git.mail.kapsi.fi>
Sat, 14 Nov 2015 20:23:48 +0000 (22:23 +0200)
Fixes compilation error with anonymous unions when
it is not the last field in message. Also fixes
extraneous newlines in header file. Cleanup the
pb.h extraneous use of ##.

generator/nanopb_generator.py
pb.h
tests/anonymous_oneof/SConscript [new file with mode: 0644]
tests/anonymous_oneof/decode_oneof.c [new file with mode: 0644]
tests/anonymous_oneof/oneof.proto [new file with mode: 0644]

index a21257b..1321746 100755 (executable)
@@ -492,6 +492,9 @@ class Field:
 
         return result
 
+    def get_last_field_name(self):
+        return self.name
+
     def largest_field_value(self):
         '''Determine if this field needs 16bit or 32bit pb_field_t structure to compile properly.
         Returns numeric value or a C-expression for assert.'''
@@ -704,12 +707,18 @@ class OneOf(Field):
         return None
 
     def tags(self):
-        return '\n'.join([f.tags() for f in self.fields])
+        return ''.join([f.tags() for f in self.fields])
 
     def pb_field_t(self, prev_field_name):
         result = ',\n'.join([f.pb_field_t(prev_field_name) for f in self.fields])
         return result
 
+    def get_last_field_name(self):
+        if self.anonymous:
+            return self.fields[-1].name
+        else:
+            return self.name + '.' + self.fields[-1].name
+
     def largest_field_value(self):
         largest = FieldMaxSize()
         for f in self.fields:
@@ -860,10 +869,7 @@ class Message:
         for field in self.ordered_fields:
             result += field.pb_field_t(prev)
             result += ',\n'
-            if isinstance(field, OneOf):
-                prev = field.name + '.' + field.fields[-1].name
-            else:
-                prev = field.name
+            prev = field.get_last_field_name()
 
         result += '    PB_LAST_FIELD\n};'
         return result
diff --git a/pb.h b/pb.h
index 0286b85..8b9338c 100644 (file)
--- a/pb.h
+++ b/pb.h
@@ -518,7 +518,7 @@ struct pb_extension_s {
     pb_membersize(st, u.m[0]), 0, ptr}
 
 #define PB_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \
-        PB_ ## ONEOF_ ## allocation(union_name, tag, message, field, \
+        PB_ONEOF_ ## allocation(union_name, tag, message, field, \
         PB_DATAOFFSET_ ## placement(message, union_name.field, prevfield), \
         PB_LTYPE_MAP_ ## type, ptr)
 
@@ -533,7 +533,7 @@ struct pb_extension_s {
     pb_membersize(st, m[0]), 0, ptr}
 
 #define PB_ANONYMOUS_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \
-        PB_ ## ANONYMOUS_ONEOF_ ## allocation(union_name, tag, message, field, \
+        PB_ANONYMOUS_ONEOF_ ## allocation(union_name, tag, message, field, \
         PB_DATAOFFSET_ ## placement(message, field, prevfield), \
         PB_LTYPE_MAP_ ## type, ptr)
 
diff --git a/tests/anonymous_oneof/SConscript b/tests/anonymous_oneof/SConscript
new file mode 100644 (file)
index 0000000..8f634bd
--- /dev/null
@@ -0,0 +1,29 @@
+# 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 = 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()
+        env2.Append(CFLAGS = "-Wno-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 (file)
index 0000000..0f774db
--- /dev/null
@@ -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 (file)
index 0000000..d56285c
--- /dev/null
@@ -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;
+}
+