More unittests
authorPetteri Aimonen <jpa@npb.mail.kapsi.fi>
Tue, 23 Aug 2011 18:50:09 +0000 (18:50 +0000)
committerPetteri Aimonen <jpa@npb.mail.kapsi.fi>
Tue, 23 Aug 2011 18:50:09 +0000 (18:50 +0000)
git-svn-id: https://svn.kapsi.fi/jpa/nanopb@966 e3a754e5-d11d-0410-8d38-ebb782a927b9

docs/concepts.rst
docs/lsr.css
docs/reference.rst
pb_encode.c
tests/encode_unittests.c
tests/unittestproto.proto [new file with mode: 0644]

index fac9061..e607640 100644 (file)
@@ -176,9 +176,13 @@ Encoding callbacks
 
 When encoding, the callback should write out complete fields, including the wire type and field number tag. It can write as many or as few fields as it likes. For example, if you want to write out an array as *repeated* field, you should do it all in a single call.
 
-If the callback is used in a submessage, it will be called multiple times during a single call to `pb_encode`_. It must produce the same amount of data every time. If the callback is directly in the main message, it is called only once.
+Usually you can use `pb_encode_tag_for_field`_ to encode the wire type and tag number of the field. However, if you want to encode a repeated field as a packed array, you must call `pb_encode_tag`_ instead to specify a wire type of *PB_WT_STRING*.
+
+If the callback is used in a submessage, it will be called multiple times during a single call to `pb_encode`_. In this case, it must produce the same amount of data every time. If the callback is directly in the main message, it is called only once.
 
 .. _`pb_encode`: reference.html#pb-encode
+.. _`pb_encode_tag_for_field`: reference.html#pb-encode-tag-for-field
+.. _`pb_encode_tag`: reference.html#pb-encode-tag
 
 This callback writes out a dynamically sized string::
 
@@ -207,7 +211,7 @@ This callback reads multiple integers and prints them::
 
     bool read_ints(pb_istream_t *stream, const pb_field_t *field, void *arg)
     {
-        while (stream.bytes_left)
+        while (stream->bytes_left)
         {
             uint64_t value;
             if (!pb_decode_varint(stream, &value))
index 8605325..429bce5 100644 (file)
@@ -191,7 +191,6 @@ table.docutils {
   text-align: left;
   border: 1px solid gray;
   border-collapse: collapse;
-  width: 100%;
   margin: 1.5em 0em;
 }
 
@@ -211,6 +210,11 @@ th.field-name {
 table.docutils th {
   font-family: monospace;
   background-color: #f6f6f6;
+  vertical-align: middle;
+}
+
+table.field-list {
+  border: none;  
 }
 
 div.sidebar {
index 2fc3d48..55ade5e 100644 (file)
@@ -37,6 +37,7 @@ PB_HTYPE_REQUIRED    0x00  Verify that field exists in decoded message.
 PB_HTYPE_OPTIONAL    0x10  Use separate *has_<field>* boolean to specify
                            whether the field is present.
 PB_HTYPE_ARRAY       0x20  A repeated field with preallocated array.
+                           Separate *<field>_count* for number of items.
 PB_HTYPE_CALLBACK    0x30  A field with dynamic storage size, data is
                            actually a pointer to a structure containing a
                            callback function.
@@ -183,6 +184,17 @@ Same as `pb_encode_tag`_, except takes the parameters from a *pb_field_t* struct
 
 This function only considers the LTYPE of the field. You can use it from your field callbacks, because the source generator writes correct LTYPE also for callback type fields.
 
+Wire type mapping is as follows:
+
+========================= ============
+LTYPEs                    Wire type
+========================= ============
+VARINT, SVARINT           PB_WT_VARINT
+FIXED with data_size == 8 PB_WT_64BIT  
+STRING, BYTES, SUBMESSAGE PB_WT_STRING 
+FIXED with data_size == 4 PB_WT_32BIT
+========================= ============
+
 pb_encode_string
 ----------------
 Writes the length of a string as varint and then contents of the string. Used for writing fields with wire type PB_WT_STRING. ::
index d333cfd..e83e068 100644 (file)
@@ -81,7 +81,7 @@ static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *fie
     if (count == 0)
         return true;
     
-    if (PB_LTYPE(field->type) < PB_LTYPE_LAST_PACKABLE)
+    if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
     {
         if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
             return false;
index 5578a0a..b8664d9 100644 (file)
@@ -15,6 +15,24 @@ bool streamcallback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
     return true;
 }
 
+bool fieldcallback(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
+{
+    int value = 0x55;
+    if (!pb_encode_tag_for_field(stream, field))
+        return false;
+    return pb_encode_varint(stream, value);
+}
+
+bool crazyfieldcallback(pb_ostream_t *stream, const pb_field_t *field, const void *arg)
+{
+    /* This callback writes different amount of data the second time. */
+    uint32_t *state = (uint32_t*)arg;
+    *state <<= 8;
+    if (!pb_encode_tag_for_field(stream, field))
+        return false;
+    return pb_encode_varint(stream, *state);
+}
+
 /* Check that expression x writes data y.
  * Y is a string, which may contain null bytes. Null terminator is ignored.
  */
@@ -80,6 +98,17 @@ int main()
         
         COMMENT("Test pb_encode_tag_for_field")
         TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x50"));
+        
+        field.type = PB_LTYPE_FIXED;
+        field.data_size = 8;
+        TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x51"));
+        
+        field.type = PB_LTYPE_STRING;
+        TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x52"));
+        
+        field.type = PB_LTYPE_FIXED;
+        field.data_size = 4;
+        TEST(WRITES(pb_encode_tag_for_field(&s, &field), "\x55"));
     }
     
     {
@@ -181,8 +210,67 @@ int main()
     }
     
     {
+        uint8_t buffer[10];
+        pb_ostream_t s;
+        FloatArray msg = {1, {99.0f}};
+        
+        COMMENT("Test pb_encode with float array")
+        
+        TEST(WRITES(pb_encode(&s, FloatArray_fields, &msg),
+                    "\x0A\x04\x00\x00\xc6\x42"))
+        
+        msg.data_count = 0;
+        TEST(WRITES(pb_encode(&s, FloatArray_fields, &msg), ""))
+        
+        msg.data_count = 3;
+        TEST(!pb_encode(&s, FloatArray_fields, &msg))
+    }
+    
+    {
+        uint8_t buffer[10];
+        pb_ostream_t s;
+        CallbackArray msg;
+        
+        msg.data.funcs.encode = &fieldcallback;
+        
+        COMMENT("Test pb_encode with callback field.")
+        TEST(WRITES(pb_encode(&s, CallbackArray_fields, &msg), "\x08\x55"))
+    }
+    
+    {
+        uint8_t buffer[10];
+        pb_ostream_t s;
+        IntegerContainer msg = {{5, {1,2,3,4,5}}};
+        
+        COMMENT("Test pb_encode with packed array in a submessage.")
+        TEST(WRITES(pb_encode(&s, IntegerContainer_fields, &msg),
+                    "\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"))
+    }
+    
+    {
+        uint8_t buffer[10];
+        pb_ostream_t s;
+        CallbackContainer msg;
+        CallbackContainerContainer msg2;
+        uint32_t state = 1;
+        
+        msg.submsg.data.funcs.encode = &fieldcallback;
+        msg2.submsg.submsg.data.funcs.encode = &fieldcallback;
+        
+        COMMENT("Test pb_encode with callback field in a submessage.")
+        TEST(WRITES(pb_encode(&s, CallbackContainer_fields, &msg), "\x0A\x02\x08\x55"))
+        TEST(WRITES(pb_encode(&s, CallbackContainerContainer_fields, &msg2),
+                    "\x0A\x04\x0A\x02\x08\x55"))
         
+        /* Misbehaving callback */
+        msg.submsg.data.funcs.encode = &crazyfieldcallback;
+        msg.submsg.data.arg = &state;
+        msg2.submsg.submsg.data.funcs.encode = &crazyfieldcallback;
+        msg2.submsg.submsg.data.arg = &state;
         
+        TEST(!pb_encode(&s, CallbackContainer_fields, &msg))
+        state = 1;
+        TEST(!pb_encode(&s, CallbackContainerContainer_fields, &msg2))
     }
     
     if (status != 0)
diff --git a/tests/unittestproto.proto b/tests/unittestproto.proto
new file mode 100644 (file)
index 0000000..c8a39dd
--- /dev/null
@@ -0,0 +1,28 @@
+import 'nanopb.proto';
+
+message IntegerArray {
+    repeated int32 data = 1 [(nanopb).max_count = 10];
+}
+
+message FloatArray {
+    repeated float data = 1 [(nanopb).max_count = 10];
+}
+
+message CallbackArray {
+    // We cheat a bit and use this message for testing other types, too.
+    // Nanopb does not care about the actual defined data type for callback
+    // fields.
+    repeated int32 data = 1;
+}
+
+message IntegerContainer {
+    required IntegerArray submsg = 1;
+}
+
+message CallbackContainer {
+    required CallbackArray submsg = 1;
+}
+
+message CallbackContainerContainer {
+    required CallbackContainer submsg = 1;
+}