Compare commits
16 Commits
dev_extens
...
dev_tests_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f93d39f72 | ||
|
|
d395768c8d | ||
|
|
b9f14bddf7 | ||
|
|
e2e9980627 | ||
|
|
0bbcb7b367 | ||
|
|
696a01bf14 | ||
|
|
f04ab838ab | ||
|
|
e681dd0d75 | ||
|
|
4dccf28ba9 | ||
|
|
262c62676c | ||
|
|
d7f3a74388 | ||
|
|
4aef194a99 | ||
|
|
81cf30034b | ||
|
|
dd72698508 | ||
|
|
f15093e8bd | ||
|
|
b663909fb6 |
20
.gitignore
vendored
20
.gitignore
vendored
@@ -7,6 +7,7 @@
|
|||||||
*.pb
|
*.pb
|
||||||
*~
|
*~
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
|
.sconsign.dblite
|
||||||
julkaisu.txt
|
julkaisu.txt
|
||||||
docs/*.html
|
docs/*.html
|
||||||
docs/generator_flow.png
|
docs/generator_flow.png
|
||||||
@@ -18,22 +19,3 @@ example_avr_double/test_conversions
|
|||||||
example_unions/decode
|
example_unions/decode
|
||||||
example_unions/encode
|
example_unions/encode
|
||||||
generator/nanopb_pb2.pyc
|
generator/nanopb_pb2.pyc
|
||||||
tests/decode_unittests
|
|
||||||
tests/encode_unittests
|
|
||||||
tests/test_compiles
|
|
||||||
tests/test_decode1
|
|
||||||
tests/test_decode2
|
|
||||||
tests/test_decode3
|
|
||||||
tests/test_decode3_buf
|
|
||||||
tests/test_decode_callbacks
|
|
||||||
tests/test_encode1
|
|
||||||
tests/test_encode2
|
|
||||||
tests/test_encode3
|
|
||||||
tests/test_encode3_buf
|
|
||||||
tests/test_encode_callbacks
|
|
||||||
tests/test_missing_fields
|
|
||||||
tests/test_multiple_files
|
|
||||||
tests/bc_decode
|
|
||||||
tests/bc_encode
|
|
||||||
tests/breakpoints
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
nanopb-0.2.2
|
||||||
|
Add support for extension fields (issue 17)
|
||||||
|
Fix unknown fields in empty message (issue 78)
|
||||||
|
Include the field tags in the generated .pb.h file.
|
||||||
|
Add pb_decode_delimited and pb_encode_delimited wrapper functions (issue 74)
|
||||||
|
Add a section in top of pb.h for changing compilation settings (issue 76)
|
||||||
|
Documentation improvements (issues 12, 77 and others)
|
||||||
|
Improved tests
|
||||||
|
|
||||||
nanopb-0.2.1
|
nanopb-0.2.1
|
||||||
NOTE: The default callback function signature has changed.
|
NOTE: The default callback function signature has changed.
|
||||||
If you don't want to update your code, define PB_OLD_CALLBACK_STYLE.
|
If you don't want to update your code, define PB_OLD_CALLBACK_STYLE.
|
||||||
|
|||||||
94
compat/pb_syshdr.h
Normal file
94
compat/pb_syshdr.h
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/* This is an example of a header file for platforms/compilers that do
|
||||||
|
* not come with stdint.h/stddef.h/stdbool.h/string.h. To use it, define
|
||||||
|
* PB_SYSTEM_HEADER as "pb_syshdr.h", including the quotes, and add the
|
||||||
|
* compat folder to your include path.
|
||||||
|
*
|
||||||
|
* It is very likely that you will need to customize this file to suit
|
||||||
|
* your platform. For any compiler that supports C99, this file should
|
||||||
|
* not be necessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PB_SYSHDR_H_
|
||||||
|
#define _PB_SYSHDR_H_
|
||||||
|
|
||||||
|
/* stdint.h subset */
|
||||||
|
#ifdef HAVE_STDINT_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#else
|
||||||
|
/* You will need to modify these to match the word size of your platform. */
|
||||||
|
typedef signed char int8_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef signed short int16_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef signed int int32_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
typedef signed long long int64_t;
|
||||||
|
typedef unsigned long long uint64_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* stddef.h subset */
|
||||||
|
#ifdef HAVE_STDDEF_H
|
||||||
|
#include <stddef.h>
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef uint32_t size_t;
|
||||||
|
#define offsetof(st, m) ((size_t)(&((st *)0)->m))
|
||||||
|
|
||||||
|
#ifndef NULL
|
||||||
|
#define NULL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* stdbool.h subset */
|
||||||
|
#ifdef HAVE_STDBOOL_H
|
||||||
|
#include <stdbool.h>
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifndef __cplusplus
|
||||||
|
typedef int bool;
|
||||||
|
#define false 0
|
||||||
|
#define true 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* string.h subset */
|
||||||
|
#ifdef HAVE_STRING_H
|
||||||
|
#include <string.h>
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* Implementations are from the Public Domain C Library (PDCLib). */
|
||||||
|
static size_t strlen( const char * s )
|
||||||
|
{
|
||||||
|
size_t rc = 0;
|
||||||
|
while ( s[rc] )
|
||||||
|
{
|
||||||
|
++rc;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void * memcpy( void *s1, const void *s2, size_t n )
|
||||||
|
{
|
||||||
|
char * dest = (char *) s1;
|
||||||
|
const char * src = (const char *) s2;
|
||||||
|
while ( n-- )
|
||||||
|
{
|
||||||
|
*dest++ = *src++;
|
||||||
|
}
|
||||||
|
return s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void * memset( void * s, int c, size_t n )
|
||||||
|
{
|
||||||
|
unsigned char * p = (unsigned char *) s;
|
||||||
|
while ( n-- )
|
||||||
|
{
|
||||||
|
*p++ = (unsigned char) c;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -256,6 +256,50 @@ generates this field description array for the structure *Person_PhoneNumber*::
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Extension fields
|
||||||
|
================
|
||||||
|
Protocol Buffers supports a concept of `extension fields`_, which are
|
||||||
|
additional fields to a message, but defined outside the actual message.
|
||||||
|
The definition can even be in a completely separate .proto file.
|
||||||
|
|
||||||
|
The base message is declared as extensible by keyword *extensions* in
|
||||||
|
the .proto file::
|
||||||
|
|
||||||
|
message MyMessage {
|
||||||
|
.. fields ..
|
||||||
|
extensions 100 to 199;
|
||||||
|
}
|
||||||
|
|
||||||
|
For each extensible message, *nanopb_generator.py* declares an additional
|
||||||
|
callback field called *extensions*. The field and associated datatype
|
||||||
|
*pb_extension_t* forms a linked list of handlers. When an unknown field is
|
||||||
|
encountered, the decoder calls each handler in turn until either one of them
|
||||||
|
handles the field, or the list is exhausted.
|
||||||
|
|
||||||
|
The actual extensions are declared using the *extend* keyword in the .proto,
|
||||||
|
and are in the global namespace::
|
||||||
|
|
||||||
|
extend MyMessage {
|
||||||
|
optional int32 myextension = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
For each extension, *nanopb_generator.py* creates a constant of type
|
||||||
|
*pb_extension_type_t*. To link together the base message and the extension,
|
||||||
|
you have to:
|
||||||
|
|
||||||
|
1. Allocate storage for your field, matching the datatype in the .proto.
|
||||||
|
For example, for a *int32* field, you need a *int32_t* variable to store
|
||||||
|
the value.
|
||||||
|
2. Create a *pb_extension_t* constant, with pointers to your variable and
|
||||||
|
to the generated *pb_extension_type_t*.
|
||||||
|
3. Set the *message.extensions* pointer to point to the *pb_extension_t*.
|
||||||
|
|
||||||
|
An example of this is available in *tests/test_encode_extensions.c* and
|
||||||
|
*tests/test_decode_extensions.c*.
|
||||||
|
|
||||||
|
.. _`extension fields`: https://developers.google.com/protocol-buffers/docs/proto#extensions
|
||||||
|
|
||||||
|
|
||||||
Return values and error handling
|
Return values and error handling
|
||||||
================================
|
================================
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ Features and limitations
|
|||||||
#) Allows specifying maximum size for strings and arrays, so that they can be allocated statically.
|
#) Allows specifying maximum size for strings and arrays, so that they can be allocated statically.
|
||||||
#) No malloc needed: everything can be allocated statically or on the stack.
|
#) No malloc needed: everything can be allocated statically or on the stack.
|
||||||
#) You can use either encoder or decoder alone to cut the code size in half.
|
#) You can use either encoder or decoder alone to cut the code size in half.
|
||||||
#) Support for most protobuf features, including: all data types, nested submessages, default values, repeated and optional fields, packed arrays.
|
#) Support for most protobuf features, including: all data types, nested submessages, default values, repeated and optional fields, packed arrays, extension fields.
|
||||||
#) Callback mechanism for handling messages larger than can fit in available RAM.
|
#) Callback mechanism for handling messages larger than can fit in available RAM.
|
||||||
#) Extensive set of tests.
|
#) Extensive set of tests.
|
||||||
|
|
||||||
@@ -103,6 +103,8 @@ Nanopb should compile with most ansi-C compatible compilers. It however requires
|
|||||||
|
|
||||||
If these header files do not come with your compiler, you should be able to find suitable replacements online. Mostly the requirements are very simple, just a few basic functions and typedefs.
|
If these header files do not come with your compiler, you should be able to find suitable replacements online. Mostly the requirements are very simple, just a few basic functions and typedefs.
|
||||||
|
|
||||||
|
Alternatively, you can define *PB_SYSTEM_HEADER*, which should be the name of a single header file including all the necessary definitions.
|
||||||
|
|
||||||
Debugging and testing
|
Debugging and testing
|
||||||
=====================
|
=====================
|
||||||
Extensive unittests are included under the *tests* folder. Just type *make* there to run the tests.
|
Extensive unittests are included under the *tests* folder. Just type *make* there to run the tests.
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ PB_BUFFER_ONLY Disables the support for custom streams. Only
|
|||||||
PB_OLD_CALLBACK_STYLE Use the old function signature (void\* instead
|
PB_OLD_CALLBACK_STYLE Use the old function signature (void\* instead
|
||||||
of void\*\*) for callback fields. This was the
|
of void\*\*) for callback fields. This was the
|
||||||
default until nanopb-0.2.1.
|
default until nanopb-0.2.1.
|
||||||
|
PB_SYSTEM_HEADER Replace the standard header files with a single
|
||||||
|
header file. It should define all the required
|
||||||
|
functions and typedefs listed on the
|
||||||
|
`overview page`_. Value must include quotes,
|
||||||
|
for example *#define PB_SYSTEM_HEADER "foo.h"*.
|
||||||
============================ ================================================
|
============================ ================================================
|
||||||
|
|
||||||
The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow
|
The PB_MAX_REQUIRED_FIELDS, PB_FIELD_16BIT and PB_FIELD_32BIT settings allow
|
||||||
@@ -56,7 +61,7 @@ raising some datatype limits to suit larger messages. Their need is recognized
|
|||||||
automatically by C-preprocessor #if-directives in the generated .pb.h files.
|
automatically by C-preprocessor #if-directives in the generated .pb.h files.
|
||||||
The default setting is to use the smallest datatypes (least resources used).
|
The default setting is to use the smallest datatypes (least resources used).
|
||||||
|
|
||||||
|
.. _`overview page`: index.html#compiler-requirements
|
||||||
|
|
||||||
|
|
||||||
Proto file options
|
Proto file options
|
||||||
@@ -299,6 +304,41 @@ Protocol Buffers wire types. These are used with `pb_encode_tag`_. ::
|
|||||||
PB_WT_32BIT = 5
|
PB_WT_32BIT = 5
|
||||||
} pb_wire_type_t;
|
} pb_wire_type_t;
|
||||||
|
|
||||||
|
pb_extension_type_t
|
||||||
|
-------------------
|
||||||
|
Defines the handler functions and auxiliary data for a field that extends
|
||||||
|
another message. Usually autogenerated by *nanopb_generator.py*::
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
|
||||||
|
uint32_t tag, pb_wire_type_t wire_type);
|
||||||
|
bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
|
||||||
|
const void *arg;
|
||||||
|
} pb_extension_type_t;
|
||||||
|
|
||||||
|
In the normal case, the function pointers are *NULL* and the decoder and
|
||||||
|
encoder use their internal implementations. The internal implementations
|
||||||
|
assume that *arg* points to a *pb_field_t* that describes the field in question.
|
||||||
|
|
||||||
|
To implement custom processing of unknown fields, you can provide pointers
|
||||||
|
to your own functions. Their functionality is mostly the same as for normal
|
||||||
|
callback fields, except that they get called for any unknown field when decoding.
|
||||||
|
|
||||||
|
pb_extension_t
|
||||||
|
--------------
|
||||||
|
Ties together the extension field type and the storage for the field value::
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const pb_extension_type_t *type;
|
||||||
|
void *dest;
|
||||||
|
pb_extension_t *next;
|
||||||
|
} pb_extension_t;
|
||||||
|
|
||||||
|
:type: Pointer to the structure that defines the callback functions.
|
||||||
|
:dest: Pointer to the variable that stores the field value
|
||||||
|
(as used by the default extension callback functions.)
|
||||||
|
:next: Pointer to the next extension handler, or *NULL*.
|
||||||
|
|
||||||
PB_GET_ERROR
|
PB_GET_ERROR
|
||||||
------------
|
------------
|
||||||
Get the current error message from a stream, or a placeholder string if
|
Get the current error message from a stream, or a placeholder string if
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
|
'''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.'''
|
||||||
nanopb_version = "nanopb-0.2.2-dev"
|
nanopb_version = "nanopb-0.2.3-dev"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import google.protobuf.descriptor_pb2 as descriptor
|
import google.protobuf.descriptor_pb2 as descriptor
|
||||||
@@ -310,18 +310,26 @@ class ExtensionField(Field):
|
|||||||
Field.__init__(self, self.fullname + 'struct', desc, field_options)
|
Field.__init__(self, self.fullname + 'struct', desc, field_options)
|
||||||
|
|
||||||
if self.rules != 'OPTIONAL':
|
if self.rules != 'OPTIONAL':
|
||||||
raise NotImplementedError("Only 'optional' is supported for extension fields. "
|
self.skip = True
|
||||||
+ "(%s.rules == %s)" % (self.fullname, self.rules))
|
else:
|
||||||
|
self.skip = False
|
||||||
self.rules = 'OPTEXT'
|
self.rules = 'OPTEXT'
|
||||||
|
|
||||||
def extension_decl(self):
|
def extension_decl(self):
|
||||||
'''Declaration of the extension type in the .pb.h file'''
|
'''Declaration of the extension type in the .pb.h file'''
|
||||||
|
if self.skip:
|
||||||
|
msg = '/* Extension field %s was skipped because only "optional"\n' % self.fullname
|
||||||
|
msg +=' type of extension fields is currently supported. */\n'
|
||||||
|
return msg
|
||||||
|
|
||||||
return 'extern const pb_extension_type_t %s;\n' % self.fullname
|
return 'extern const pb_extension_type_t %s;\n' % self.fullname
|
||||||
|
|
||||||
def extension_def(self):
|
def extension_def(self):
|
||||||
'''Definition of the extension type in the .pb.c file'''
|
'''Definition of the extension type in the .pb.c file'''
|
||||||
|
|
||||||
|
if self.skip:
|
||||||
|
return ''
|
||||||
|
|
||||||
result = 'typedef struct {\n'
|
result = 'typedef struct {\n'
|
||||||
result += str(self)
|
result += str(self)
|
||||||
result += '\n} %s;\n\n' % self.struct_name
|
result += '\n} %s;\n\n' % self.struct_name
|
||||||
@@ -475,6 +483,7 @@ def parse_file(fdesc, file_options):
|
|||||||
|
|
||||||
for names, extension in iterate_extensions(fdesc, base_name):
|
for names, extension in iterate_extensions(fdesc, base_name):
|
||||||
field_options = get_nanopb_suboptions(extension, file_options, names)
|
field_options = get_nanopb_suboptions(extension, file_options, names)
|
||||||
|
if field_options.type != nanopb_pb2.FT_IGNORE:
|
||||||
extensions.append(ExtensionField(names, extension, field_options))
|
extensions.append(ExtensionField(names, extension, field_options))
|
||||||
|
|
||||||
# Fix field default values where enum short names are used.
|
# Fix field default values where enum short names are used.
|
||||||
|
|||||||
13
pb.h
13
pb.h
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
/* Version of the nanopb library. Just in case you want to check it in
|
/* Version of the nanopb library. Just in case you want to check it in
|
||||||
* your own program. */
|
* your own program. */
|
||||||
#define NANOPB_VERSION nanopb-0.2.2-dev
|
#define NANOPB_VERSION nanopb-0.2.3-dev
|
||||||
|
|
||||||
/* Include all the system headers needed by nanopb. You will need the
|
/* Include all the system headers needed by nanopb. You will need the
|
||||||
* definitions of the following:
|
* definitions of the following:
|
||||||
@@ -215,6 +215,17 @@ struct _pb_field_t {
|
|||||||
} pb_packed;
|
} pb_packed;
|
||||||
PB_PACKED_STRUCT_END
|
PB_PACKED_STRUCT_END
|
||||||
|
|
||||||
|
/* Make sure that the standard integer types are of the expected sizes.
|
||||||
|
* All kinds of things may break otherwise.. atleast all fixed* types. */
|
||||||
|
STATIC_ASSERT(sizeof(int8_t) == 1, INT8_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(uint8_t) == 1, UINT8_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(int16_t) == 2, INT16_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(uint16_t) == 2, UINT16_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(int32_t) == 4, INT32_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(uint32_t) == 4, UINT32_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(int64_t) == 8, INT64_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(uint64_t) == 8, UINT64_T_WRONG_SIZE)
|
||||||
|
|
||||||
/* This structure is used for 'bytes' arrays.
|
/* This structure is used for 'bytes' arrays.
|
||||||
* It has the number of bytes in the beginning, and after that an array.
|
* It has the number of bytes in the beginning, and after that an array.
|
||||||
* Note that actual structs used will have a different length of bytes array.
|
* Note that actual structs used will have a different length of bytes array.
|
||||||
|
|||||||
66
pb_decode.h
66
pb_decode.h
@@ -12,6 +12,39 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Structure for defining custom input streams. You will need to provide
|
||||||
|
* a callback function to read the bytes from your storage, which can be
|
||||||
|
* for example a file or a network socket.
|
||||||
|
*
|
||||||
|
* The callback must conform to these rules:
|
||||||
|
*
|
||||||
|
* 1) Return false on IO errors. This will cause decoding to abort.
|
||||||
|
* 2) You can use state to store your own data (e.g. buffer pointer),
|
||||||
|
* and rely on pb_read to verify that no-body reads past bytes_left.
|
||||||
|
* 3) Your callback may be used with substreams, in which case bytes_left
|
||||||
|
* is different than from the main stream. Don't use bytes_left to compute
|
||||||
|
* any pointers.
|
||||||
|
*/
|
||||||
|
struct _pb_istream_t
|
||||||
|
{
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
/* Callback pointer is not used in buffer-only configuration.
|
||||||
|
* Having an int pointer here allows binary compatibility but
|
||||||
|
* gives an error if someone tries to assign callback function.
|
||||||
|
*/
|
||||||
|
int *callback;
|
||||||
|
#else
|
||||||
|
bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *state; /* Free field for use by callback implementation */
|
||||||
|
size_t bytes_left;
|
||||||
|
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
const char *errmsg;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
/***************************
|
/***************************
|
||||||
* Main decoding functions *
|
* Main decoding functions *
|
||||||
***************************/
|
***************************/
|
||||||
@@ -66,39 +99,6 @@ pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize);
|
|||||||
*/
|
*/
|
||||||
bool pb_read(pb_istream_t *stream, uint8_t *buf, size_t count);
|
bool pb_read(pb_istream_t *stream, uint8_t *buf, size_t count);
|
||||||
|
|
||||||
/* Structure for defining custom input streams. You will need to provide
|
|
||||||
* a callback function to read the bytes from your storage, which can be
|
|
||||||
* for example a file or a network socket.
|
|
||||||
*
|
|
||||||
* The callback must conform to these rules:
|
|
||||||
*
|
|
||||||
* 1) Return false on IO errors. This will cause decoding to abort.
|
|
||||||
* 2) You can use state to store your own data (e.g. buffer pointer),
|
|
||||||
* and rely on pb_read to verify that no-body reads past bytes_left.
|
|
||||||
* 3) Your callback may be used with substreams, in which case bytes_left
|
|
||||||
* is different than from the main stream. Don't use bytes_left to compute
|
|
||||||
* any pointers.
|
|
||||||
*/
|
|
||||||
struct _pb_istream_t
|
|
||||||
{
|
|
||||||
#ifdef PB_BUFFER_ONLY
|
|
||||||
/* Callback pointer is not used in buffer-only configuration.
|
|
||||||
* Having an int pointer here allows binary compatibility but
|
|
||||||
* gives an error if someone tries to assign callback function.
|
|
||||||
*/
|
|
||||||
int *callback;
|
|
||||||
#else
|
|
||||||
bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void *state; /* Free field for use by callback implementation */
|
|
||||||
size_t bytes_left;
|
|
||||||
|
|
||||||
#ifndef PB_NO_ERRMSG
|
|
||||||
const char *errmsg;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
* Helper functions for writing field callbacks *
|
* Helper functions for writing field callbacks *
|
||||||
|
|||||||
68
pb_encode.h
68
pb_encode.h
@@ -12,6 +12,40 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Structure for defining custom output streams. You will need to provide
|
||||||
|
* a callback function to write the bytes to your storage, which can be
|
||||||
|
* for example a file or a network socket.
|
||||||
|
*
|
||||||
|
* The callback must conform to these rules:
|
||||||
|
*
|
||||||
|
* 1) Return false on IO errors. This will cause encoding to abort.
|
||||||
|
* 2) You can use state to store your own data (e.g. buffer pointer).
|
||||||
|
* 3) pb_write will update bytes_written after your callback runs.
|
||||||
|
* 4) Substreams will modify max_size and bytes_written. Don't use them
|
||||||
|
* to calculate any pointers.
|
||||||
|
*/
|
||||||
|
struct _pb_ostream_t
|
||||||
|
{
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
/* Callback pointer is not used in buffer-only configuration.
|
||||||
|
* Having an int pointer here allows binary compatibility but
|
||||||
|
* gives an error if someone tries to assign callback function.
|
||||||
|
* Also, NULL pointer marks a 'sizing stream' that does not
|
||||||
|
* write anything.
|
||||||
|
*/
|
||||||
|
int *callback;
|
||||||
|
#else
|
||||||
|
bool (*callback)(pb_ostream_t *stream, const uint8_t *buf, size_t count);
|
||||||
|
#endif
|
||||||
|
void *state; /* Free field for use by callback implementation. */
|
||||||
|
size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */
|
||||||
|
size_t bytes_written; /* Number of bytes written so far. */
|
||||||
|
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
const char *errmsg;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
/***************************
|
/***************************
|
||||||
* Main encoding functions *
|
* Main encoding functions *
|
||||||
***************************/
|
***************************/
|
||||||
@@ -70,40 +104,6 @@ pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize);
|
|||||||
*/
|
*/
|
||||||
bool pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count);
|
bool pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count);
|
||||||
|
|
||||||
/* Structure for defining custom output streams. You will need to provide
|
|
||||||
* a callback function to write the bytes to your storage, which can be
|
|
||||||
* for example a file or a network socket.
|
|
||||||
*
|
|
||||||
* The callback must conform to these rules:
|
|
||||||
*
|
|
||||||
* 1) Return false on IO errors. This will cause encoding to abort.
|
|
||||||
* 2) You can use state to store your own data (e.g. buffer pointer).
|
|
||||||
* 3) pb_write will update bytes_written after your callback runs.
|
|
||||||
* 4) Substreams will modify max_size and bytes_written. Don't use them
|
|
||||||
* to calculate any pointers.
|
|
||||||
*/
|
|
||||||
struct _pb_ostream_t
|
|
||||||
{
|
|
||||||
#ifdef PB_BUFFER_ONLY
|
|
||||||
/* Callback pointer is not used in buffer-only configuration.
|
|
||||||
* Having an int pointer here allows binary compatibility but
|
|
||||||
* gives an error if someone tries to assign callback function.
|
|
||||||
* Also, NULL pointer marks a 'sizing stream' that does not
|
|
||||||
* write anything.
|
|
||||||
*/
|
|
||||||
int *callback;
|
|
||||||
#else
|
|
||||||
bool (*callback)(pb_ostream_t *stream, const uint8_t *buf, size_t count);
|
|
||||||
#endif
|
|
||||||
void *state; /* Free field for use by callback implementation. */
|
|
||||||
size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */
|
|
||||||
size_t bytes_written; /* Number of bytes written so far. */
|
|
||||||
|
|
||||||
#ifndef PB_NO_ERRMSG
|
|
||||||
const char *errmsg;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************
|
/************************************************
|
||||||
* Helper functions for writing field callbacks *
|
* Helper functions for writing field callbacks *
|
||||||
|
|||||||
143
tests/Makefile
143
tests/Makefile
@@ -1,143 +1,6 @@
|
|||||||
CFLAGS=-ansi -Wall -Werror -I .. -g -O0
|
all:
|
||||||
DEPS=../pb_decode.h ../pb_encode.h ../pb.h person.pb.h \
|
scons
|
||||||
callbacks2.pb.h callbacks.pb.h unittests.h unittestproto.pb.h \
|
|
||||||
alltypes.pb.h missing_fields.pb.h
|
|
||||||
TESTS= decode_unittests encode_unittests \
|
|
||||||
test_decode1 test_decode2 test_decode3 test_decode3_buf \
|
|
||||||
test_encode1 test_encode2 test_encode3 test_encode3_buf \
|
|
||||||
test_decode_callbacks test_encode_callbacks \
|
|
||||||
test_missing_fields test_no_messages test_funny_name \
|
|
||||||
test_multiple_files test_cxxcompile test_options \
|
|
||||||
bc_encode bc_decode test_encode_extensions test_decode_extensions
|
|
||||||
|
|
||||||
# More strict checks for the core part of nanopb
|
|
||||||
CC_VERSION=$(shell $(CC) -v 2>&1)
|
|
||||||
CFLAGS_CORE=
|
|
||||||
ifneq (,$(findstring gcc,$(CC_VERSION)))
|
|
||||||
CFLAGS_CORE=-pedantic -Wextra -Wcast-qual -Wlogical-op -Wconversion
|
|
||||||
CFLAGS+=--coverage -fstack-protector-all
|
|
||||||
LDFLAGS+=--coverage
|
|
||||||
endif
|
|
||||||
ifneq (,$(findstring clang,$(CC_VERSION)))
|
|
||||||
CFLAGS_CORE=-pedantic -Wextra -Wcast-qual -Wconversion
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Also use mudflap if it is available
|
|
||||||
# To enable, run with make -B USE_MUDFLAP=y
|
|
||||||
USE_MUDFLAP ?= n
|
|
||||||
ifeq ($(USE_MUDFLAP),y)
|
|
||||||
CFLAGS += -fmudflap
|
|
||||||
LDFLAGS += -lmudflap -fmudflap
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: breakpoints $(TESTS) run_unittests
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TESTS) person.pb* alltypes.pb* *.o *.gcda *.gcno *.pb.h *.pb.c
|
scons -c
|
||||||
|
|
||||||
%.pb.o: %.pb.c %.pb.h
|
|
||||||
$(CC) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
%.o: %.c $(DEPS)
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
pb_encode.o: ../pb_encode.c $(DEPS)
|
|
||||||
$(CC) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
|
|
||||||
pb_decode.o: ../pb_decode.c $(DEPS)
|
|
||||||
$(CC) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
|
|
||||||
|
|
||||||
# Test for compilability with c++ compiler
|
|
||||||
|
|
||||||
pb_encode.cxx.o: ../pb_encode.c $(DEPS)
|
|
||||||
$(CXX) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
|
|
||||||
pb_decode.cxx.o: ../pb_decode.c $(DEPS)
|
|
||||||
$(CXX) $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
|
|
||||||
|
|
||||||
# Test for PB_BUF_ONLY compilation option
|
|
||||||
|
|
||||||
pb_encode.buf.o: ../pb_encode.c $(DEPS)
|
|
||||||
$(CC) -DPB_BUFFER_ONLY $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
|
|
||||||
pb_decode.buf.o: ../pb_decode.c $(DEPS)
|
|
||||||
$(CC) -DPB_BUFFER_ONLY $(CFLAGS) $(CFLAGS_CORE) -c -o $@ $<
|
|
||||||
%.buf.o: %.c $(DEPS)
|
|
||||||
$(CC) -DPB_BUFFER_ONLY $(CFLAGS) -c -o $@ $<
|
|
||||||
test_encode3_buf: test_encode3.buf.o pb_encode.buf.o alltypes.pb.o
|
|
||||||
$(CC) $(LDFLAGS) $^ -o $@
|
|
||||||
test_decode3_buf: test_decode3.buf.o pb_decode.buf.o alltypes.pb.o
|
|
||||||
$(CC) $(LDFLAGS) $^ -o $@
|
|
||||||
|
|
||||||
test_cxxcompile: pb_encode.cxx.o pb_decode.cxx.o
|
|
||||||
test_decode1: test_decode1.o pb_decode.o person.pb.o
|
|
||||||
test_decode2: test_decode2.o pb_decode.o person.pb.o
|
|
||||||
test_decode3: test_decode3.o pb_decode.o alltypes.pb.o
|
|
||||||
test_encode1: test_encode1.o pb_encode.o person.pb.o
|
|
||||||
test_encode2: test_encode2.o pb_encode.o person.pb.o
|
|
||||||
test_encode3: test_encode3.o pb_encode.o alltypes.pb.o
|
|
||||||
test_multiple_files: test_multiple_files.o pb_encode.o callbacks2.pb.o callbacks.pb.o
|
|
||||||
test_decode_callbacks: test_decode_callbacks.o pb_decode.o callbacks.pb.o
|
|
||||||
test_encode_callbacks: test_encode_callbacks.o pb_encode.o callbacks.pb.o
|
|
||||||
test_missing_fields: test_missing_fields.o pb_encode.o pb_decode.o missing_fields.pb.o
|
|
||||||
decode_unittests: decode_unittests.o pb_decode.o unittestproto.pb.o
|
|
||||||
encode_unittests: encode_unittests.o pb_encode.o unittestproto.pb.o
|
|
||||||
test_no_messages: no_messages.pb.h no_messages.pb.c no_messages.pb.o
|
|
||||||
test_funny_name: funny-proto+name.pb.h funny-proto+name.pb.o
|
|
||||||
bc_encode: bc_alltypes.pb.o pb_encode.o bc_encode.o
|
|
||||||
bc_decode: bc_alltypes.pb.o pb_decode.o bc_decode.o
|
|
||||||
test_encode_extensions: test_encode_extensions.c pb_encode.o alltypes.pb.o extensions.pb.o
|
|
||||||
test_decode_extensions: test_decode_extensions.c pb_decode.o alltypes.pb.o extensions.pb.o
|
|
||||||
|
|
||||||
%.pb: %.proto
|
|
||||||
protoc -I. -I../generator -I/usr/include -o$@ $<
|
|
||||||
|
|
||||||
%.pb.c %.pb.h: %.pb ../generator/nanopb_generator.py
|
|
||||||
python ../generator/nanopb_generator.py $<
|
|
||||||
|
|
||||||
breakpoints: ../*.c *.c
|
|
||||||
grep -n 'return false;' $^ | cut -d: -f-2 | xargs -n 1 echo b > $@
|
|
||||||
|
|
||||||
coverage: run_unittests
|
|
||||||
gcov pb_encode.gcda
|
|
||||||
gcov pb_decode.gcda
|
|
||||||
|
|
||||||
run_unittests: $(TESTS)
|
|
||||||
rm -f *.gcda
|
|
||||||
|
|
||||||
./decode_unittests > /dev/null
|
|
||||||
./encode_unittests > /dev/null
|
|
||||||
|
|
||||||
[ "`./test_encode1 | ./test_decode1`" = \
|
|
||||||
"`./test_encode1 | protoc --decode=Person -I. -I../generator -I/usr/include person.proto`" ]
|
|
||||||
|
|
||||||
[ "`./test_encode2 | ./test_decode1`" = \
|
|
||||||
"`./test_encode2 | protoc --decode=Person -I. -I../generator -I/usr/include person.proto`" ]
|
|
||||||
|
|
||||||
[ "`./test_encode2 | ./test_decode2`" = \
|
|
||||||
"`./test_encode2 | protoc --decode=Person -I. -I../generator -I/usr/include person.proto`" ]
|
|
||||||
|
|
||||||
[ "`./test_decode2 < person_with_extra_field.pb`" = \
|
|
||||||
"`./test_encode2 | ./test_decode2`" ]
|
|
||||||
|
|
||||||
[ "`./test_encode_callbacks | ./test_decode_callbacks`" = \
|
|
||||||
"`./test_encode_callbacks | protoc --decode=TestMessage callbacks.proto`" ]
|
|
||||||
|
|
||||||
./test_encode3 | ./test_decode3
|
|
||||||
./test_encode3 1 | ./test_decode3 1
|
|
||||||
./test_encode3 1 | protoc --decode=AllTypes -I. -I../generator -I/usr/include alltypes.proto >/dev/null
|
|
||||||
./test_encode3_buf 1 | ./test_decode3_buf 1
|
|
||||||
./test_decode3 < alltypes_with_extra_fields.pb
|
|
||||||
./bc_encode | ./bc_decode
|
|
||||||
./test_encode_extensions | ./test_decode_extensions
|
|
||||||
|
|
||||||
./test_missing_fields
|
|
||||||
|
|
||||||
test_options: options.pb.h options.expected options.pb.o
|
|
||||||
cat options.expected | while read -r p; do \
|
|
||||||
if ! grep -q "$$p" $<; then \
|
|
||||||
echo Expected: "$$p"; \
|
|
||||||
exit 1; \
|
|
||||||
fi \
|
|
||||||
done
|
|
||||||
|
|
||||||
run_fuzztest: test_decode3
|
|
||||||
bash -c 'ulimit -c unlimited; I=1; while true; do cat /dev/urandom | ./test_decode3 > /dev/null; I=$$(($$I+1)); echo -en "\r$$I"; done'
|
|
||||||
|
|||||||
109
tests/SConstruct
Normal file
109
tests/SConstruct
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
Help('''
|
||||||
|
Type 'scons' to build and run all the available test cases.
|
||||||
|
It will automatically detect your platform and C compiler and
|
||||||
|
build appropriately.
|
||||||
|
|
||||||
|
You can modify the behavious using following options:
|
||||||
|
CC Name of C compiler
|
||||||
|
CXX Name of C++ compiler
|
||||||
|
CCFLAGS Flags to pass to the C compiler
|
||||||
|
CXXFLAGS Flags to pass to the C++ compiler
|
||||||
|
|
||||||
|
For example, for a clang build, use:
|
||||||
|
scons CC=clang CXX=clang++
|
||||||
|
''')
|
||||||
|
|
||||||
|
import os
|
||||||
|
env = Environment(ENV = os.environ)
|
||||||
|
|
||||||
|
# Allow overriding the compiler with scons CC=???
|
||||||
|
if 'CC' in ARGUMENTS: env.Replace(CC = ARGUMENTS['CC'])
|
||||||
|
if 'CXX' in ARGUMENTS: env.Replace(CXX = ARGUMENTS['CXX'])
|
||||||
|
if 'CFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CFLAGS'])
|
||||||
|
if 'CXXFLAGS' in ARGUMENTS: env.Append(CCFLAGS = ARGUMENTS['CXXFLAGS'])
|
||||||
|
|
||||||
|
# Add the builders defined in site_init.py
|
||||||
|
add_nanopb_builders(env)
|
||||||
|
|
||||||
|
# Path to the files shared by tests, and to the nanopb core.
|
||||||
|
env.Append(CPPPATH = ["#../", "#common"])
|
||||||
|
|
||||||
|
# Path for finding nanopb.proto
|
||||||
|
env.Append(PROTOCPATH = '#../generator')
|
||||||
|
|
||||||
|
# Check the compilation environment, unless we are just cleaning up.
|
||||||
|
if not env.GetOption('clean'):
|
||||||
|
conf = Configure(env)
|
||||||
|
|
||||||
|
# If the platform doesn't support C99, use our own header file instead.
|
||||||
|
stdbool = conf.CheckCHeader('stdbool.h')
|
||||||
|
stdint = conf.CheckCHeader('stdint.h')
|
||||||
|
stddef = conf.CheckCHeader('stddef.h')
|
||||||
|
string = conf.CheckCHeader('string.h')
|
||||||
|
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 = "#../compat")
|
||||||
|
|
||||||
|
if stdbool: conf.env.Append(CPPDEFINES = {'HAVE_STDBOOL_H': 1})
|
||||||
|
if stdint: conf.env.Append(CPPDEFINES = {'HAVE_STDINT_H': 1})
|
||||||
|
if stddef: conf.env.Append(CPPDEFINES = {'HAVE_STDDEF_H': 1})
|
||||||
|
if string: conf.env.Append(CPPDEFINES = {'HAVE_STRING_H': 1})
|
||||||
|
|
||||||
|
# Check if we can use pkg-config to find protobuf include path
|
||||||
|
status, output = conf.TryAction('pkg-config protobuf --variable=includedir > $TARGET')
|
||||||
|
if status:
|
||||||
|
conf.env.Append(PROTOCPATH = output.strip())
|
||||||
|
else:
|
||||||
|
conf.env.Append(PROTOCPATH = '/usr/include')
|
||||||
|
|
||||||
|
# Check if libmudflap is available (only with GCC)
|
||||||
|
if 'gcc' in env['CC']:
|
||||||
|
if conf.CheckLib('mudflap'):
|
||||||
|
conf.env.Append(CCFLAGS = '-fmudflap')
|
||||||
|
conf.env.Append(LINKFLAGS = '-lmudflap -fmudflap')
|
||||||
|
|
||||||
|
# End the config stuff
|
||||||
|
env = conf.Finish()
|
||||||
|
|
||||||
|
# Initialize the CCFLAGS according to the compiler
|
||||||
|
if 'gcc' in env['CC']:
|
||||||
|
# GNU Compiler Collection
|
||||||
|
|
||||||
|
# Debug info, warnings as errors
|
||||||
|
env.Append(CFLAGS = '-ansi -pedantic -g -O0 -Wall -Werror --coverage -fstack-protector-all')
|
||||||
|
env.Append(LINKFLAGS = '--coverage')
|
||||||
|
|
||||||
|
# More strict checks on the nanopb core
|
||||||
|
env.Append(CORECFLAGS = '-Wextra -Wcast-qual -Wlogical-op -Wconversion')
|
||||||
|
elif 'clang' in env['CC']:
|
||||||
|
# CLang
|
||||||
|
env.Append(CFLAGS = '-ansi -pedantic -g -O0 -Wall -Werror')
|
||||||
|
env.Append(CORECFLAGS = ' -Wextra -Wcast-qual -Wconversion')
|
||||||
|
elif 'cl' in env['CC']:
|
||||||
|
# Microsoft Visual C++
|
||||||
|
|
||||||
|
# Debug info on, warning level 2 for tests, warnings as errors
|
||||||
|
env.Append(CFLAGS = '/Zi /W2 /WX')
|
||||||
|
env.Append(LINKFLAGS = '/DEBUG')
|
||||||
|
|
||||||
|
# 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')
|
||||||
|
|
||||||
|
env.SetDefault(CORECFLAGS = '')
|
||||||
|
|
||||||
|
if 'clang++' in env['CXX']:
|
||||||
|
env.Append(CXXFLAGS = '-g -O0 -Wall -Werror -Wextra -Wno-missing-field-initializers')
|
||||||
|
elif 'g++' in env['CXX']:
|
||||||
|
env.Append(CXXFLAGS = '-g -O0 -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
|
||||||
|
SConscript(Glob('*/SConscript'), exports = 'env')
|
||||||
|
|
||||||
12
tests/alltypes/SConscript
Normal file
12
tests/alltypes/SConscript
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Build and run a test that encodes and decodes a message that contains
|
||||||
|
# all of the Protocol Buffers data types.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
env.NanopbProto("alltypes")
|
||||||
|
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"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_alltypes.output"])
|
||||||
|
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include "alltypes.pb.h"
|
#include "alltypes.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
#define TEST(x) if (!(x)) { \
|
#define TEST(x) if (!(x)) { \
|
||||||
printf("Test " #x " failed.\n"); \
|
printf("Test " #x " failed.\n"); \
|
||||||
@@ -176,15 +177,19 @@ bool check_alltypes(pb_istream_t *stream, int mode)
|
|||||||
|
|
||||||
int main(int argc, char **argv)
|
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. */
|
/* Whether to expect the optional values or the default values. */
|
||||||
int mode = (argc > 1) ? atoi(argv[1]) : 0;
|
int mode = (argc > 1) ? atoi(argv[1]) : 0;
|
||||||
|
|
||||||
/* Read the data into buffer */
|
/* Read the data into buffer */
|
||||||
uint8_t buffer[1024];
|
SET_BINARY_MODE(stdin);
|
||||||
size_t count = fread(buffer, 1, sizeof(buffer), stdin);
|
count = fread(buffer, 1, sizeof(buffer), stdin);
|
||||||
|
|
||||||
/* Construct a pb_istream_t for reading from the buffer */
|
/* Construct a pb_istream_t for reading from the buffer */
|
||||||
pb_istream_t stream = pb_istream_from_buffer(buffer, count);
|
stream = pb_istream_from_buffer(buffer, count);
|
||||||
|
|
||||||
/* Decode and print out the stuff */
|
/* Decode and print out the stuff */
|
||||||
if (!check_alltypes(&stream, mode))
|
if (!check_alltypes(&stream, mode))
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include "alltypes.pb.h"
|
#include "alltypes.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -113,12 +114,14 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
alltypes.end = 1099;
|
alltypes.end = 1099;
|
||||||
|
|
||||||
|
{
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
|
||||||
/* Now encode it and check if we succeeded. */
|
/* Now encode it and check if we succeeded. */
|
||||||
if (pb_encode(&stream, AllTypes_fields, &alltypes))
|
if (pb_encode(&stream, AllTypes_fields, &alltypes))
|
||||||
{
|
{
|
||||||
|
SET_BINARY_MODE(stdout);
|
||||||
fwrite(buffer, 1, stream.bytes_written, stdout);
|
fwrite(buffer, 1, stream.bytes_written, stdout);
|
||||||
return 0; /* Success */
|
return 0; /* Success */
|
||||||
}
|
}
|
||||||
@@ -128,3 +131,4 @@ int main(int argc, char **argv)
|
|||||||
return 1; /* Failure */
|
return 1; /* Failure */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
11
tests/backwards_compatibility/SConscript
Normal file
11
tests/backwards_compatibility/SConscript
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Check that the old generated .pb.c/.pb.h files are still compatible with the
|
||||||
|
# current version of nanopb.
|
||||||
|
|
||||||
|
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"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_legacy.output"])
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
* incompatible changes made to the generator in future versions.
|
* incompatible changes made to the generator in future versions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "bc_alltypes.pb.h"
|
#include "alltypes_legacy.h"
|
||||||
|
|
||||||
const char SubMessage_substuff1_default[16] = "1";
|
const char SubMessage_substuff1_default[16] = "1";
|
||||||
const int32_t SubMessage_substuff2_default = 2;
|
const int32_t SubMessage_substuff2_default = 2;
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
/* Tests the decoding of all types.
|
/* Tests the decoding of all types.
|
||||||
* This is a backwards-compatibility test, using bc_alltypes.pb.h.
|
* This is a backwards-compatibility test, using alltypes_legacy.h.
|
||||||
* It is similar to test_decode3, but duplicated in order to allow
|
* It is similar to decode_alltypes, but duplicated in order to allow
|
||||||
* test_decode3 to test any new features introduced later.
|
* decode_alltypes to test any new features introduced later.
|
||||||
*
|
*
|
||||||
* Run e.g. ./bc_encode | ./bc_decode
|
* Run e.g. ./encode_legacy | ./decode_legacy
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include "bc_alltypes.pb.h"
|
#include "alltypes_legacy.h"
|
||||||
|
|
||||||
#define TEST(x) if (!(x)) { \
|
#define TEST(x) if (!(x)) { \
|
||||||
printf("Test " #x " failed.\n"); \
|
printf("Test " #x " failed.\n"); \
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
/* Attempts to test all the datatypes supported by ProtoBuf.
|
/* Attempts to test all the datatypes supported by ProtoBuf.
|
||||||
* This is a backwards-compatibility test, using bc_alltypes.pb.h.
|
* This is a backwards-compatibility test, using alltypes_legacy.h.
|
||||||
* It is similar to test_encode3, but duplicated in order to allow
|
* It is similar to encode_alltypes, but duplicated in order to allow
|
||||||
* test_encode3 to test any new features introduced later.
|
* encode_alltypes to test any new features introduced later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include "bc_alltypes.pb.h"
|
#include "alltypes_legacy.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -114,12 +115,14 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
alltypes.end = 1099;
|
alltypes.end = 1099;
|
||||||
|
|
||||||
|
{
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
|
||||||
/* Now encode it and check if we succeeded. */
|
/* Now encode it and check if we succeeded. */
|
||||||
if (pb_encode(&stream, AllTypes_fields, &alltypes))
|
if (pb_encode(&stream, AllTypes_fields, &alltypes))
|
||||||
{
|
{
|
||||||
|
SET_BINARY_MODE(stdout);
|
||||||
fwrite(buffer, 1, stream.bytes_written, stdout);
|
fwrite(buffer, 1, stream.bytes_written, stdout);
|
||||||
return 0; /* Success */
|
return 0; /* Success */
|
||||||
}
|
}
|
||||||
@@ -129,3 +132,4 @@ int main(int argc, char **argv)
|
|||||||
return 1; /* Failure */
|
return 1; /* Failure */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
12
tests/basic_buffer/SConscript
Normal file
12
tests/basic_buffer/SConscript
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Build and run a basic round-trip test using memory buffer encoding.
|
||||||
|
|
||||||
|
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"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_buffer.output"])
|
||||||
|
env.Decode(["encode_buffer.output", "#common/person.proto"], MESSAGE = "Person")
|
||||||
|
env.Compare(["decode_buffer.output", "encode_buffer.decoded"])
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include "person.pb.h"
|
#include "person.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
/* This function is called once from main(), it handles
|
/* This function is called once from main(), it handles
|
||||||
the decoding and printing. */
|
the decoding and printing. */
|
||||||
@@ -59,9 +60,13 @@ bool print_person(pb_istream_t *stream)
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
/* Read the data into buffer */
|
|
||||||
uint8_t buffer[512];
|
uint8_t buffer[512];
|
||||||
size_t count = fread(buffer, 1, sizeof(buffer), stdin);
|
pb_istream_t stream;
|
||||||
|
size_t count;
|
||||||
|
|
||||||
|
/* Read the data into buffer */
|
||||||
|
SET_BINARY_MODE(stdin);
|
||||||
|
count = fread(buffer, 1, sizeof(buffer), stdin);
|
||||||
|
|
||||||
if (!feof(stdin))
|
if (!feof(stdin))
|
||||||
{
|
{
|
||||||
@@ -70,7 +75,7 @@ int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Construct a pb_istream_t for reading from the buffer */
|
/* Construct a pb_istream_t for reading from the buffer */
|
||||||
pb_istream_t stream = pb_istream_from_buffer(buffer, count);
|
stream = pb_istream_from_buffer(buffer, count);
|
||||||
|
|
||||||
/* Decode and print out the stuff */
|
/* Decode and print out the stuff */
|
||||||
if (!print_person(&stream))
|
if (!print_person(&stream))
|
||||||
@@ -6,9 +6,13 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include "person.pb.h"
|
#include "person.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
uint8_t buffer[512];
|
||||||
|
pb_ostream_t stream;
|
||||||
|
|
||||||
/* Initialize the structure with constants */
|
/* Initialize the structure with constants */
|
||||||
Person person = {"Test Person 99", 99, true, "test@person.com",
|
Person person = {"Test Person 99", 99, true, "test@person.com",
|
||||||
3, {{"555-12345678", true, Person_PhoneType_MOBILE},
|
3, {{"555-12345678", true, Person_PhoneType_MOBILE},
|
||||||
@@ -16,12 +20,13 @@ int main()
|
|||||||
{"1234-5678", true, Person_PhoneType_WORK},
|
{"1234-5678", true, Person_PhoneType_WORK},
|
||||||
}};
|
}};
|
||||||
|
|
||||||
uint8_t buffer[512];
|
stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
|
||||||
|
|
||||||
/* Now encode it and check if we succeeded. */
|
/* Now encode it and check if we succeeded. */
|
||||||
if (pb_encode(&stream, Person_fields, &person))
|
if (pb_encode(&stream, Person_fields, &person))
|
||||||
{
|
{
|
||||||
|
/* Write the result data to stdout */
|
||||||
|
SET_BINARY_MODE(stdout);
|
||||||
fwrite(buffer, 1, stream.bytes_written, stdout);
|
fwrite(buffer, 1, stream.bytes_written, stdout);
|
||||||
return 0; /* Success */
|
return 0; /* Success */
|
||||||
}
|
}
|
||||||
12
tests/basic_stream/SConscript
Normal file
12
tests/basic_stream/SConscript
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Build and run a basic round-trip test using direct stream encoding.
|
||||||
|
|
||||||
|
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"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_stream.output"])
|
||||||
|
env.Decode(["encode_stream.output", "#common/person.proto"], MESSAGE = "Person")
|
||||||
|
env.Compare(["decode_stream.output", "encode_stream.decoded"])
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include "person.pb.h"
|
#include "person.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
/* This function is called once from main(), it handles
|
/* This function is called once from main(), it handles
|
||||||
the decoding and printing.
|
the decoding and printing.
|
||||||
@@ -69,10 +70,10 @@ bool callback(pb_istream_t *stream, uint8_t *buf, size_t count)
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
/* Maximum size is specified to prevent infinite length messages from
|
pb_istream_t stream = {&callback, NULL, SIZE_MAX};
|
||||||
* hanging this in the fuzz test.
|
stream.state = stdin;
|
||||||
*/
|
SET_BINARY_MODE(stdin);
|
||||||
pb_istream_t stream = {&callback, stdin, 10000};
|
|
||||||
if (!print_person(&stream))
|
if (!print_person(&stream))
|
||||||
{
|
{
|
||||||
printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
|
printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include "person.pb.h"
|
#include "person.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
/* This binds the pb_ostream_t into the stdout stream */
|
/* This binds the pb_ostream_t into the stdout stream */
|
||||||
bool streamcallback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
|
bool streamcallback(pb_ostream_t *stream, const uint8_t *buf, size_t count)
|
||||||
@@ -22,7 +23,9 @@ int main()
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
/* Prepare the stream, output goes directly to stdout */
|
/* Prepare the stream, output goes directly to stdout */
|
||||||
pb_ostream_t stream = {&streamcallback, stdout, SIZE_MAX, 0};
|
pb_ostream_t stream = {&streamcallback, NULL, SIZE_MAX, 0};
|
||||||
|
stream.state = stdout;
|
||||||
|
SET_BINARY_MODE(stdout);
|
||||||
|
|
||||||
/* Now encode it and check if we succeeded. */
|
/* Now encode it and check if we succeeded. */
|
||||||
if (pb_encode(&stream, Person_fields, &person))
|
if (pb_encode(&stream, Person_fields, &person))
|
||||||
23
tests/buffer_only/SConscript
Normal file
23
tests/buffer_only/SConscript
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Run the alltypes test case, but compile with PB_BUFFER_ONLY=1
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
# Take copy of the files for custom build.
|
||||||
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
|
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
||||||
|
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
||||||
|
env.Command("alltypes.pb.h", "#alltypes/alltypes.pb.h", c)
|
||||||
|
env.Command("alltypes.pb.c", "#alltypes/alltypes.pb.c", c)
|
||||||
|
env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", c)
|
||||||
|
env.Command("decode_alltypes.c", "#alltypes/decode_alltypes.c", c)
|
||||||
|
|
||||||
|
# Define the compilation options
|
||||||
|
opts = env.Clone()
|
||||||
|
opts.Append(CPPDEFINES = {'PB_BUFFER_ONLY': 1})
|
||||||
|
|
||||||
|
# Now build and run the test normally.
|
||||||
|
enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode.c"])
|
||||||
|
dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode.c"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_alltypes.output"])
|
||||||
14
tests/callbacks/SConscript
Normal file
14
tests/callbacks/SConscript
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Test the functionality of the callback fields.
|
||||||
|
|
||||||
|
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"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_callbacks.output"])
|
||||||
|
|
||||||
|
env.Decode(["encode_callbacks.output", "callbacks.proto"], MESSAGE = "TestMessage")
|
||||||
|
env.Compare(["decode_callbacks.output", "encode_callbacks.decoded"])
|
||||||
|
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include "callbacks.pb.h"
|
#include "callbacks.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
bool print_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
|
bool print_string(pb_istream_t *stream, const pb_field_t *field, void **arg)
|
||||||
{
|
{
|
||||||
@@ -50,21 +51,24 @@ bool print_fixed64(pb_istream_t *stream, const pb_field_t *field, void **arg)
|
|||||||
if (!pb_decode_fixed64(stream, &value))
|
if (!pb_decode_fixed64(stream, &value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
printf((char*)*arg, (long long)value);
|
printf((char*)*arg, (long)value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
size_t length = fread(buffer, 1, 1024, stdin);
|
size_t length;
|
||||||
pb_istream_t stream = pb_istream_from_buffer(buffer, length);
|
pb_istream_t stream;
|
||||||
|
|
||||||
/* Note: empty initializer list initializes the struct with all-0.
|
/* Note: empty initializer list initializes the struct with all-0.
|
||||||
* This is recommended so that unused callbacks are set to NULL instead
|
* This is recommended so that unused callbacks are set to NULL instead
|
||||||
* of crashing at runtime.
|
* of crashing at runtime.
|
||||||
*/
|
*/
|
||||||
TestMessage testmessage = {};
|
TestMessage testmessage = {{{NULL}}};
|
||||||
|
|
||||||
|
SET_BINARY_MODE(stdin);
|
||||||
|
length = fread(buffer, 1, 1024, stdin);
|
||||||
|
stream = pb_istream_from_buffer(buffer, length);
|
||||||
|
|
||||||
testmessage.submsg.stringvalue.funcs.decode = &print_string;
|
testmessage.submsg.stringvalue.funcs.decode = &print_string;
|
||||||
testmessage.submsg.stringvalue.arg = "submsg {\n stringvalue: \"%s\"\n";
|
testmessage.submsg.stringvalue.arg = "submsg {\n stringvalue: \"%s\"\n";
|
||||||
@@ -73,7 +77,7 @@ int main()
|
|||||||
testmessage.submsg.fixed32value.funcs.decode = &print_fixed32;
|
testmessage.submsg.fixed32value.funcs.decode = &print_fixed32;
|
||||||
testmessage.submsg.fixed32value.arg = " fixed32value: %ld\n";
|
testmessage.submsg.fixed32value.arg = " fixed32value: %ld\n";
|
||||||
testmessage.submsg.fixed64value.funcs.decode = &print_fixed64;
|
testmessage.submsg.fixed64value.funcs.decode = &print_fixed64;
|
||||||
testmessage.submsg.fixed64value.arg = " fixed64value: %lld\n}\n";
|
testmessage.submsg.fixed64value.arg = " fixed64value: %ld\n}\n";
|
||||||
|
|
||||||
testmessage.stringvalue.funcs.decode = &print_string;
|
testmessage.stringvalue.funcs.decode = &print_string;
|
||||||
testmessage.stringvalue.arg = "stringvalue: \"%s\"\n";
|
testmessage.stringvalue.arg = "stringvalue: \"%s\"\n";
|
||||||
@@ -82,7 +86,7 @@ int main()
|
|||||||
testmessage.fixed32value.funcs.decode = &print_fixed32;
|
testmessage.fixed32value.funcs.decode = &print_fixed32;
|
||||||
testmessage.fixed32value.arg = "fixed32value: %ld\n";
|
testmessage.fixed32value.arg = "fixed32value: %ld\n";
|
||||||
testmessage.fixed64value.funcs.decode = &print_fixed64;
|
testmessage.fixed64value.funcs.decode = &print_fixed64;
|
||||||
testmessage.fixed64value.arg = "fixed64value: %lld\n";
|
testmessage.fixed64value.arg = "fixed64value: %ld\n";
|
||||||
testmessage.repeatedstring.funcs.decode = &print_string;
|
testmessage.repeatedstring.funcs.decode = &print_string;
|
||||||
testmessage.repeatedstring.arg = "repeatedstring: \"%s\"\n";
|
testmessage.repeatedstring.arg = "repeatedstring: \"%s\"\n";
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include "callbacks.pb.h"
|
#include "callbacks.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
bool encode_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
|
bool encode_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
|
||||||
{
|
{
|
||||||
@@ -25,19 +26,21 @@ bool encode_int32(pb_ostream_t *stream, const pb_field_t *field, void * const *a
|
|||||||
|
|
||||||
bool encode_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
|
bool encode_fixed32(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
|
||||||
{
|
{
|
||||||
|
uint32_t value = 42;
|
||||||
|
|
||||||
if (!pb_encode_tag_for_field(stream, field))
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32_t value = 42;
|
|
||||||
return pb_encode_fixed32(stream, &value);
|
return pb_encode_fixed32(stream, &value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
|
bool encode_fixed64(pb_ostream_t *stream, const pb_field_t *field, void * const *arg)
|
||||||
{
|
{
|
||||||
|
uint64_t value = 42;
|
||||||
|
|
||||||
if (!pb_encode_tag_for_field(stream, field))
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint64_t value = 42;
|
|
||||||
return pb_encode_fixed64(stream, &value);
|
return pb_encode_fixed64(stream, &value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,8 +63,10 @@ bool encode_repeatedstring(pb_ostream_t *stream, const pb_field_t *field, void *
|
|||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
uint8_t buffer[1024];
|
uint8_t buffer[1024];
|
||||||
pb_ostream_t stream = pb_ostream_from_buffer(buffer, 1024);
|
pb_ostream_t stream;
|
||||||
TestMessage testmessage = {};
|
TestMessage testmessage = {{{NULL}}};
|
||||||
|
|
||||||
|
stream = pb_ostream_from_buffer(buffer, 1024);
|
||||||
|
|
||||||
testmessage.stringvalue.funcs.encode = &encode_string;
|
testmessage.stringvalue.funcs.encode = &encode_string;
|
||||||
testmessage.int32value.funcs.encode = &encode_int32;
|
testmessage.int32value.funcs.encode = &encode_int32;
|
||||||
@@ -79,6 +84,7 @@ int main()
|
|||||||
if (!pb_encode(&stream, TestMessage_fields, &testmessage))
|
if (!pb_encode(&stream, TestMessage_fields, &testmessage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
SET_BINARY_MODE(stdout);
|
||||||
if (fwrite(buffer, stream.bytes_written, 1, stdout) != 1)
|
if (fwrite(buffer, stream.bytes_written, 1, stdout) != 1)
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
17
tests/common/SConscript
Normal file
17
tests/common/SConscript
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Build the common files needed by multiple test cases
|
||||||
|
|
||||||
|
Import('env')
|
||||||
|
|
||||||
|
# Protocol definitions for the encode/decode_unittests
|
||||||
|
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", "#../pb_decode.c")
|
||||||
|
strict.Object("pb_encode.o", "#../pb_encode.c")
|
||||||
|
|
||||||
17
tests/common/test_helpers.h
Normal file
17
tests/common/test_helpers.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/* Compatibility helpers for the test programs. */
|
||||||
|
|
||||||
|
#ifndef _TEST_HELPERS_H_
|
||||||
|
#define _TEST_HELPERS_H_
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <io.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define SET_BINARY_MODE(file)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
20
tests/cxx_main_program/SConscript
Normal file
20
tests/cxx_main_program/SConscript
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Run the alltypes test case, but compile it as C++ instead.
|
||||||
|
# In fact, compile the entire nanopb using C++ compiler.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
# Copy the files to .cxx extension in order to force C++ build.
|
||||||
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
|
env.Command("pb_encode.cxx", "#../pb_encode.c", c)
|
||||||
|
env.Command("pb_decode.cxx", "#../pb_decode.c", c)
|
||||||
|
env.Command("alltypes.pb.h", "#alltypes/alltypes.pb.h", c)
|
||||||
|
env.Command("alltypes.pb.cxx", "#alltypes/alltypes.pb.c", c)
|
||||||
|
env.Command("encode_alltypes.cxx", "#alltypes/encode_alltypes.c", c)
|
||||||
|
env.Command("decode_alltypes.cxx", "#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"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_alltypes.output"])
|
||||||
4
tests/decode_unittests/SConscript
Normal file
4
tests/decode_unittests/SConscript
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Import('env')
|
||||||
|
p = env.Program(["decode_unittests.c", "#common/unittestproto.pb.c", "#common/pb_decode.o"])
|
||||||
|
env.RunTest(p)
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@ int main()
|
|||||||
|
|
||||||
{
|
{
|
||||||
pb_istream_t s;
|
pb_istream_t s;
|
||||||
IntegerContainer dest = {};
|
IntegerContainer dest = {{0}};
|
||||||
|
|
||||||
COMMENT("Testing pb_decode_delimited")
|
COMMENT("Testing pb_decode_delimited")
|
||||||
TEST((s = S("\x09\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"),
|
TEST((s = S("\x09\x0A\x07\x0A\x05\x01\x02\x03\x04\x05"),
|
||||||
5
tests/encode_unittests/SConscript
Normal file
5
tests/encode_unittests/SConscript
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Build and run the stand-alone unit tests for the nanopb encoder part.
|
||||||
|
|
||||||
|
Import('env')
|
||||||
|
p = env.Program(["encode_unittests.c", "#common/unittestproto.pb.c", "#common/pb_encode.o"])
|
||||||
|
env.RunTest(p)
|
||||||
16
tests/extensions/SConscript
Normal file
16
tests/extensions/SConscript
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Test the support for extension fields.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
# We use the files from the alltypes test case
|
||||||
|
incpath = env.Clone()
|
||||||
|
incpath.Append(PROTOCPATH = '#alltypes')
|
||||||
|
incpath.Append(CPPPATH = '#alltypes')
|
||||||
|
|
||||||
|
incpath.NanopbProto("extensions")
|
||||||
|
enc = incpath.Program(["encode_extensions.c", "extensions.pb.c", "#alltypes/alltypes.pb$OBJSUFFIX", "#common/pb_encode.o"])
|
||||||
|
dec = incpath.Program(["decode_extensions.c", "extensions.pb.c", "#alltypes/alltypes.pb$OBJSUFFIX", "#common/pb_decode.o"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_extensions.output"])
|
||||||
|
|
||||||
58
tests/extensions/decode_extensions.c
Normal file
58
tests/extensions/decode_extensions.c
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/* Test decoding of extension fields. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <pb_decode.h>
|
||||||
|
#include "alltypes.pb.h"
|
||||||
|
#include "extensions.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
|
#define TEST(x) if (!(x)) { \
|
||||||
|
printf("Test " #x " failed.\n"); \
|
||||||
|
return 2; \
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
size_t count;
|
||||||
|
pb_istream_t stream;
|
||||||
|
|
||||||
|
AllTypes alltypes = {0};
|
||||||
|
int32_t extensionfield1;
|
||||||
|
pb_extension_t ext1;
|
||||||
|
ExtensionMessage extensionfield2;
|
||||||
|
pb_extension_t ext2;
|
||||||
|
|
||||||
|
/* Read the message data */
|
||||||
|
SET_BINARY_MODE(stdin);
|
||||||
|
count = fread(buffer, 1, sizeof(buffer), stdin);
|
||||||
|
stream = pb_istream_from_buffer(buffer, count);
|
||||||
|
|
||||||
|
/* Add the extensions */
|
||||||
|
alltypes.extensions = &ext1;
|
||||||
|
|
||||||
|
ext1.type = &AllTypes_extensionfield1;
|
||||||
|
ext1.dest = &extensionfield1;
|
||||||
|
ext1.next = &ext2;
|
||||||
|
|
||||||
|
ext2.type = &ExtensionMessage_AllTypes_extensionfield2;
|
||||||
|
ext2.dest = &extensionfield2;
|
||||||
|
ext2.next = NULL;
|
||||||
|
|
||||||
|
/* Decode the message */
|
||||||
|
if (!pb_decode(&stream, AllTypes_fields, &alltypes))
|
||||||
|
{
|
||||||
|
printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the extensions decoded properly */
|
||||||
|
TEST(extensionfield1 == 12345)
|
||||||
|
TEST(strcmp(extensionfield2.test1, "test") == 0)
|
||||||
|
TEST(extensionfield2.test2 == 54321)
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -7,25 +7,37 @@
|
|||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include "alltypes.pb.h"
|
#include "alltypes.pb.h"
|
||||||
#include "extensions.pb.h"
|
#include "extensions.pb.h"
|
||||||
|
#include "test_helpers.h"
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
AllTypes alltypes = {};
|
uint8_t buffer[1024];
|
||||||
|
pb_ostream_t stream;
|
||||||
|
|
||||||
|
AllTypes alltypes = {0};
|
||||||
int32_t extensionfield1 = 12345;
|
int32_t extensionfield1 = 12345;
|
||||||
pb_extension_t ext1 = {&AllTypes_extensionfield1, &extensionfield1, NULL};
|
pb_extension_t ext1;
|
||||||
|
ExtensionMessage extensionfield2 = {"test", 54321};
|
||||||
|
pb_extension_t ext2;
|
||||||
|
|
||||||
|
/* Set up the extensions */
|
||||||
alltypes.extensions = &ext1;
|
alltypes.extensions = &ext1;
|
||||||
|
|
||||||
ExtensionMessage extensionfield2 = {"test", 54321};
|
ext1.type = &AllTypes_extensionfield1;
|
||||||
pb_extension_t ext2 = {&ExtensionMessage_AllTypes_extensionfield2, &extensionfield2, NULL};
|
ext1.dest = &extensionfield1;
|
||||||
ext1.next = &ext2;
|
ext1.next = &ext2;
|
||||||
|
|
||||||
uint8_t buffer[1024];
|
ext2.type = &ExtensionMessage_AllTypes_extensionfield2;
|
||||||
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
ext2.dest = &extensionfield2;
|
||||||
|
ext2.next = NULL;
|
||||||
|
|
||||||
/* Now encode it and check if we succeeded. */
|
/* Set up the output stream */
|
||||||
|
stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
/* Now encode the message and check if we succeeded. */
|
||||||
if (pb_encode(&stream, AllTypes_fields, &alltypes))
|
if (pb_encode(&stream, AllTypes_fields, &alltypes))
|
||||||
{
|
{
|
||||||
|
SET_BINARY_MODE(stdout);
|
||||||
fwrite(buffer, 1, stream.bytes_written, stdout);
|
fwrite(buffer, 1, stream.bytes_written, stdout);
|
||||||
return 0; /* Success */
|
return 0; /* Success */
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,8 @@ extend AllTypes {
|
|||||||
message ExtensionMessage {
|
message ExtensionMessage {
|
||||||
extend AllTypes {
|
extend AllTypes {
|
||||||
optional ExtensionMessage AllTypes_extensionfield2 = 254;
|
optional ExtensionMessage AllTypes_extensionfield2 = 254;
|
||||||
|
required ExtensionMessage AllTypes_extensionfield3 = 253;
|
||||||
|
repeated ExtensionMessage AllTypes_extensionfield4 = 252;
|
||||||
}
|
}
|
||||||
|
|
||||||
required string test1 = 1;
|
required string test1 = 1;
|
||||||
10
tests/extra_fields/SConscript
Normal file
10
tests/extra_fields/SConscript
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Test that the decoder properly handles unknown fields in the input.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
dec = env.GetBuildPath('#basic_buffer/${PROGPREFIX}decode_buffer${PROGSUFFIX}')
|
||||||
|
env.RunTest('person_with_extra_field.output', [dec, "person_with_extra_field.pb"])
|
||||||
|
env.Compare(["person_with_extra_field.output", "person_with_extra_field.expected"])
|
||||||
|
|
||||||
|
dec2 = env.GetBuildPath('#alltypes/${PROGPREFIX}decode_alltypes${PROGSUFFIX}')
|
||||||
|
env.RunTest('alltypes_with_extra_fields.output', [dec2, 'alltypes_with_extra_fields.pb'])
|
||||||
14
tests/extra_fields/person_with_extra_field.expected
Normal file
14
tests/extra_fields/person_with_extra_field.expected
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
name: "Test Person 99"
|
||||||
|
id: 99
|
||||||
|
email: "test@person.com"
|
||||||
|
phone {
|
||||||
|
number: "555-12345678"
|
||||||
|
type: MOBILE
|
||||||
|
}
|
||||||
|
phone {
|
||||||
|
number: "99-2342"
|
||||||
|
}
|
||||||
|
phone {
|
||||||
|
number: "1234-5678"
|
||||||
|
type: WORK
|
||||||
|
}
|
||||||
24
tests/field_size_16/SConscript
Normal file
24
tests/field_size_16/SConscript
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Run the alltypes test case, but compile with PB_FIELD_16BIT=1.
|
||||||
|
# Also the .proto file has been modified to have high indexes.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
# Take copy of the files for custom build.
|
||||||
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
|
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
||||||
|
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
||||||
|
env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", c)
|
||||||
|
env.Command("decode_alltypes.c", "#alltypes/decode_alltypes.c", c)
|
||||||
|
|
||||||
|
env.NanopbProto("alltypes")
|
||||||
|
|
||||||
|
# Define the compilation options
|
||||||
|
opts = env.Clone()
|
||||||
|
opts.Append(CPPDEFINES = {'PB_FIELD_16BIT': 1})
|
||||||
|
|
||||||
|
# Now build and run the test normally.
|
||||||
|
enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode.c"])
|
||||||
|
dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode.c"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_alltypes.output"])
|
||||||
3
tests/field_size_16/alltypes.options
Normal file
3
tests/field_size_16/alltypes.options
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
* max_size:16
|
||||||
|
* max_count:5
|
||||||
|
|
||||||
90
tests/field_size_16/alltypes.proto
Normal file
90
tests/field_size_16/alltypes.proto
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
message SubMessage {
|
||||||
|
required string substuff1 = 1 [default = "1"];
|
||||||
|
required int32 substuff2 = 2 [default = 2];
|
||||||
|
optional fixed32 substuff3 = 65535 [default = 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
message EmptyMessage {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
required EmptyMessage req_emptymsg = 18;
|
||||||
|
|
||||||
|
|
||||||
|
repeated int32 rep_int32 = 21;
|
||||||
|
repeated int64 rep_int64 = 22;
|
||||||
|
repeated uint32 rep_uint32 = 23;
|
||||||
|
repeated uint64 rep_uint64 = 24;
|
||||||
|
repeated sint32 rep_sint32 = 25;
|
||||||
|
repeated sint64 rep_sint64 = 26;
|
||||||
|
repeated bool rep_bool = 27;
|
||||||
|
|
||||||
|
repeated fixed32 rep_fixed32 = 28;
|
||||||
|
repeated sfixed32 rep_sfixed32= 29;
|
||||||
|
repeated float rep_float = 30;
|
||||||
|
|
||||||
|
repeated fixed64 rep_fixed64 = 10031;
|
||||||
|
repeated sfixed64 rep_sfixed64= 10032;
|
||||||
|
repeated double rep_double = 10033;
|
||||||
|
|
||||||
|
repeated string rep_string = 10034;
|
||||||
|
repeated bytes rep_bytes = 10035;
|
||||||
|
repeated SubMessage rep_submsg = 10036;
|
||||||
|
repeated MyEnum rep_enum = 10037;
|
||||||
|
repeated EmptyMessage rep_emptymsg = 10038;
|
||||||
|
|
||||||
|
optional int32 opt_int32 = 10041 [default = 4041];
|
||||||
|
optional int64 opt_int64 = 10042 [default = 4042];
|
||||||
|
optional uint32 opt_uint32 = 10043 [default = 4043];
|
||||||
|
optional uint64 opt_uint64 = 10044 [default = 4044];
|
||||||
|
optional sint32 opt_sint32 = 10045 [default = 4045];
|
||||||
|
optional sint64 opt_sint64 = 10046 [default = 4046];
|
||||||
|
optional bool opt_bool = 10047 [default = false];
|
||||||
|
|
||||||
|
optional fixed32 opt_fixed32 = 10048 [default = 4048];
|
||||||
|
optional sfixed32 opt_sfixed32= 10049 [default = 4049];
|
||||||
|
optional float opt_float = 10050 [default = 4050];
|
||||||
|
|
||||||
|
optional fixed64 opt_fixed64 = 10051 [default = 4051];
|
||||||
|
optional sfixed64 opt_sfixed64= 10052 [default = 4052];
|
||||||
|
optional double opt_double = 10053 [default = 4053];
|
||||||
|
|
||||||
|
optional string opt_string = 10054 [default = "4054"];
|
||||||
|
optional bytes opt_bytes = 10055 [default = "4055"];
|
||||||
|
optional SubMessage opt_submsg = 10056;
|
||||||
|
optional MyEnum opt_enum = 10057 [default = Second];
|
||||||
|
optional EmptyMessage opt_emptymsg = 10058;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
24
tests/field_size_32/SConscript
Normal file
24
tests/field_size_32/SConscript
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Run the alltypes test case, but compile with PB_FIELD_32BIT=1.
|
||||||
|
# Also the .proto file has been modified to have high indexes.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
# Take copy of the files for custom build.
|
||||||
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
|
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
||||||
|
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
||||||
|
env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", c)
|
||||||
|
env.Command("decode_alltypes.c", "#alltypes/decode_alltypes.c", c)
|
||||||
|
|
||||||
|
env.NanopbProto("alltypes")
|
||||||
|
|
||||||
|
# Define the compilation options
|
||||||
|
opts = env.Clone()
|
||||||
|
opts.Append(CPPDEFINES = {'PB_FIELD_32BIT': 1})
|
||||||
|
|
||||||
|
# Now build and run the test normally.
|
||||||
|
enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode.c"])
|
||||||
|
dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode.c"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_alltypes.output"])
|
||||||
3
tests/field_size_32/alltypes.options
Normal file
3
tests/field_size_32/alltypes.options
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
* max_size:16
|
||||||
|
* max_count:5
|
||||||
|
|
||||||
90
tests/field_size_32/alltypes.proto
Normal file
90
tests/field_size_32/alltypes.proto
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
message SubMessage {
|
||||||
|
required string substuff1 = 1 [default = "1"];
|
||||||
|
required int32 substuff2 = 2 [default = 2];
|
||||||
|
optional fixed32 substuff3 = 12365535 [default = 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
message EmptyMessage {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
required EmptyMessage req_emptymsg = 18;
|
||||||
|
|
||||||
|
|
||||||
|
repeated int32 rep_int32 = 21;
|
||||||
|
repeated int64 rep_int64 = 22;
|
||||||
|
repeated uint32 rep_uint32 = 23;
|
||||||
|
repeated uint64 rep_uint64 = 24;
|
||||||
|
repeated sint32 rep_sint32 = 25;
|
||||||
|
repeated sint64 rep_sint64 = 26;
|
||||||
|
repeated bool rep_bool = 27;
|
||||||
|
|
||||||
|
repeated fixed32 rep_fixed32 = 28;
|
||||||
|
repeated sfixed32 rep_sfixed32= 29;
|
||||||
|
repeated float rep_float = 30;
|
||||||
|
|
||||||
|
repeated fixed64 rep_fixed64 = 10031;
|
||||||
|
repeated sfixed64 rep_sfixed64= 10032;
|
||||||
|
repeated double rep_double = 10033;
|
||||||
|
|
||||||
|
repeated string rep_string = 10034;
|
||||||
|
repeated bytes rep_bytes = 10035;
|
||||||
|
repeated SubMessage rep_submsg = 10036;
|
||||||
|
repeated MyEnum rep_enum = 10037;
|
||||||
|
repeated EmptyMessage rep_emptymsg = 10038;
|
||||||
|
|
||||||
|
optional int32 opt_int32 = 10041 [default = 4041];
|
||||||
|
optional int64 opt_int64 = 10042 [default = 4042];
|
||||||
|
optional uint32 opt_uint32 = 10043 [default = 4043];
|
||||||
|
optional uint64 opt_uint64 = 10044 [default = 4044];
|
||||||
|
optional sint32 opt_sint32 = 10045 [default = 4045];
|
||||||
|
optional sint64 opt_sint64 = 10046 [default = 4046];
|
||||||
|
optional bool opt_bool = 10047 [default = false];
|
||||||
|
|
||||||
|
optional fixed32 opt_fixed32 = 10048 [default = 4048];
|
||||||
|
optional sfixed32 opt_sfixed32= 10049 [default = 4049];
|
||||||
|
optional float opt_float = 10050 [default = 4050];
|
||||||
|
|
||||||
|
optional fixed64 opt_fixed64 = 10051 [default = 4051];
|
||||||
|
optional sfixed64 opt_sfixed64= 10052 [default = 4052];
|
||||||
|
optional double opt_double = 10053 [default = 4053];
|
||||||
|
|
||||||
|
optional string opt_string = 10054 [default = "4054"];
|
||||||
|
optional bytes opt_bytes = 10055 [default = "4055"];
|
||||||
|
optional SubMessage opt_submsg = 10056;
|
||||||
|
optional MyEnum opt_enum = 10057 [default = Second];
|
||||||
|
optional EmptyMessage opt_emptymsg = 10058;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
8
tests/missing_fields/SConscript
Normal file
8
tests/missing_fields/SConscript
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Check that the decoder properly detects when required fields are missing.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
env.NanopbProto("missing_fields")
|
||||||
|
test = env.Program(["missing_fields.c", "missing_fields.pb.c", "#common/pb_encode.o", "#common/pb_decode.o"])
|
||||||
|
env.RunTest(test)
|
||||||
|
|
||||||
@@ -7,12 +7,13 @@
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
uint8_t buffer[512] = {};
|
uint8_t buffer[512];
|
||||||
|
|
||||||
/* Create a message with one missing field */
|
/* Create a message with one missing field */
|
||||||
{
|
{
|
||||||
MissingField msg = {};
|
MissingField msg = {0};
|
||||||
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
|
||||||
if (!pb_encode(&stream, MissingField_fields, &msg))
|
if (!pb_encode(&stream, MissingField_fields, &msg))
|
||||||
{
|
{
|
||||||
printf("Encode failed.\n");
|
printf("Encode failed.\n");
|
||||||
@@ -22,7 +23,7 @@ int main()
|
|||||||
|
|
||||||
/* Test that it decodes properly if we don't require that field */
|
/* Test that it decodes properly if we don't require that field */
|
||||||
{
|
{
|
||||||
MissingField msg = {};
|
MissingField msg = {0};
|
||||||
pb_istream_t stream = pb_istream_from_buffer(buffer, sizeof(buffer));
|
pb_istream_t stream = pb_istream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
|
||||||
if (!pb_decode(&stream, MissingField_fields, &msg))
|
if (!pb_decode(&stream, MissingField_fields, &msg))
|
||||||
@@ -34,7 +35,7 @@ int main()
|
|||||||
|
|
||||||
/* Test that it does *not* decode properly if we require the field */
|
/* Test that it does *not* decode properly if we require the field */
|
||||||
{
|
{
|
||||||
AllFields msg = {};
|
AllFields msg = {0};
|
||||||
pb_istream_t stream = pb_istream_from_buffer(buffer, sizeof(buffer));
|
pb_istream_t stream = pb_istream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
|
||||||
if (pb_decode(&stream, AllFields_fields, &msg))
|
if (pb_decode(&stream, AllFields_fields, &msg))
|
||||||
13
tests/multiple_files/SConscript
Normal file
13
tests/multiple_files/SConscript
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Test that multiple .proto files don't cause name collisions.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
incpath = env.Clone()
|
||||||
|
incpath.Append(PROTOCPATH = '#multiple_files')
|
||||||
|
|
||||||
|
incpath.NanopbProto("callbacks")
|
||||||
|
incpath.NanopbProto("callbacks2")
|
||||||
|
test = incpath.Program(["test_multiple_files.c", "callbacks.pb.c", "callbacks2.pb.c"])
|
||||||
|
|
||||||
|
env.RunTest(test)
|
||||||
|
|
||||||
16
tests/multiple_files/callbacks.proto
Normal file
16
tests/multiple_files/callbacks.proto
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
message SubMessage {
|
||||||
|
optional string stringvalue = 1;
|
||||||
|
repeated int32 int32value = 2;
|
||||||
|
repeated fixed32 fixed32value = 3;
|
||||||
|
repeated fixed64 fixed64value = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TestMessage {
|
||||||
|
optional string stringvalue = 1;
|
||||||
|
repeated int32 int32value = 2;
|
||||||
|
repeated fixed32 fixed32value = 3;
|
||||||
|
repeated fixed64 fixed64value = 4;
|
||||||
|
optional SubMessage submsg = 5;
|
||||||
|
repeated string repeatedstring = 6;
|
||||||
|
}
|
||||||
|
|
||||||
12
tests/multiple_files/test_multiple_files.c
Normal file
12
tests/multiple_files/test_multiple_files.c
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Tests if this still compiles when multiple .proto files are involved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <pb_encode.h>
|
||||||
|
#include "callbacks2.pb.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
23
tests/no_errmsg/SConscript
Normal file
23
tests/no_errmsg/SConscript
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Run the alltypes test case, but compile with PB_NO_ERRMSG=1
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
# Take copy of the files for custom build.
|
||||||
|
c = Copy("$TARGET", "$SOURCE")
|
||||||
|
env.Command("pb_encode.c", "#../pb_encode.c", c)
|
||||||
|
env.Command("pb_decode.c", "#../pb_decode.c", c)
|
||||||
|
env.Command("alltypes.pb.h", "#alltypes/alltypes.pb.h", c)
|
||||||
|
env.Command("alltypes.pb.c", "#alltypes/alltypes.pb.c", c)
|
||||||
|
env.Command("encode_alltypes.c", "#alltypes/encode_alltypes.c", c)
|
||||||
|
env.Command("decode_alltypes.c", "#alltypes/decode_alltypes.c", c)
|
||||||
|
|
||||||
|
# Define the compilation options
|
||||||
|
opts = env.Clone()
|
||||||
|
opts.Append(CPPDEFINES = {'PB_NO_ERRMSG': 1})
|
||||||
|
|
||||||
|
# Now build and run the test normally.
|
||||||
|
enc = opts.Program(["encode_alltypes.c", "alltypes.pb.c", "pb_encode.c"])
|
||||||
|
dec = opts.Program(["decode_alltypes.c", "alltypes.pb.c", "pb_decode.c"])
|
||||||
|
|
||||||
|
env.RunTest(enc)
|
||||||
|
env.RunTest([dec, "encode_alltypes.output"])
|
||||||
7
tests/no_messages/SConscript
Normal file
7
tests/no_messages/SConscript
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Test that a .proto file without any messages compiles fine.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
env.NanopbProto("no_messages")
|
||||||
|
env.Object('no_messages.pb.c')
|
||||||
|
|
||||||
9
tests/options/SConscript
Normal file
9
tests/options/SConscript
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Test that the generator options work as expected.
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
env.NanopbProto("options")
|
||||||
|
env.Object('options.pb.c')
|
||||||
|
|
||||||
|
env.Match(['options.pb.h', 'options.expected'])
|
||||||
|
|
||||||
111
tests/site_scons/site_init.py
Normal file
111
tests/site_scons/site_init.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Make terminal colors work on windows
|
||||||
|
import colorama
|
||||||
|
colorama.init()
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add_nanopb_builders(env):
|
||||||
|
'''Add the necessary builder commands for nanopb tests.'''
|
||||||
|
|
||||||
|
# Build command for building .pb from .proto using protoc
|
||||||
|
def proto_actions(source, target, env, for_signature):
|
||||||
|
esc = env['ESCAPE']
|
||||||
|
dirs = ' '.join(['-I' + esc(env.GetBuildPath(d)) for d in env['PROTOCPATH']])
|
||||||
|
return '$PROTOC $PROTOCFLAGS %s -o%s %s' % (dirs, esc(str(target[0])), esc(str(source[0])))
|
||||||
|
|
||||||
|
proto_file_builder = Builder(generator = proto_actions,
|
||||||
|
suffix = '.pb',
|
||||||
|
src_suffix = '.proto')
|
||||||
|
env.Append(BUILDERS = {'Proto': proto_file_builder})
|
||||||
|
env.SetDefault(PROTOC = 'protoc')
|
||||||
|
env.SetDefault(PROTOCPATH = ['.'])
|
||||||
|
|
||||||
|
# Build command for running nanopb generator
|
||||||
|
import os.path
|
||||||
|
def nanopb_targets(target, source, env):
|
||||||
|
basename = os.path.splitext(str(source[0]))[0]
|
||||||
|
target.append(basename + '.pb.h')
|
||||||
|
return target, source
|
||||||
|
|
||||||
|
nanopb_file_builder = Builder(action = '$NANOPB_GENERATOR $NANOPB_FLAGS $SOURCE',
|
||||||
|
suffix = '.pb.c',
|
||||||
|
src_suffix = '.pb',
|
||||||
|
emitter = nanopb_targets)
|
||||||
|
env.Append(BUILDERS = {'Nanopb': nanopb_file_builder})
|
||||||
|
env.SetDefault(NANOPB_GENERATOR = 'python ' + env.GetBuildPath("#../generator/nanopb_generator.py"))
|
||||||
|
env.SetDefault(NANOPB_FLAGS = '-q')
|
||||||
|
|
||||||
|
# Combined method to run both protoc and nanopb generator
|
||||||
|
def run_protoc_and_nanopb(env, source):
|
||||||
|
b1 = env.Proto(source)
|
||||||
|
b2 = env.Nanopb(source)
|
||||||
|
return b1 + b2
|
||||||
|
env.AddMethod(run_protoc_and_nanopb, "NanopbProto")
|
||||||
|
|
||||||
|
# Build command that runs a test program and saves the output
|
||||||
|
def run_test(target, source, env):
|
||||||
|
if len(source) > 1:
|
||||||
|
infile = open(str(source[1]))
|
||||||
|
else:
|
||||||
|
infile = None
|
||||||
|
|
||||||
|
pipe = subprocess.Popen(str(source[0]),
|
||||||
|
stdin = infile,
|
||||||
|
stdout = open(str(target[0]), 'w'),
|
||||||
|
stderr = sys.stderr)
|
||||||
|
result = pipe.wait()
|
||||||
|
if result == 0:
|
||||||
|
print '\033[32m[ OK ]\033[0m Ran ' + str(source[0])
|
||||||
|
else:
|
||||||
|
print '\033[31m[FAIL]\033[0m Program ' + str(source[0]) + ' returned ' + str(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
run_test_builder = Builder(action = run_test,
|
||||||
|
suffix = '.output')
|
||||||
|
env.Append(BUILDERS = {'RunTest': run_test_builder})
|
||||||
|
|
||||||
|
# Build command that decodes a message using protoc
|
||||||
|
def decode_actions(source, target, env, for_signature):
|
||||||
|
dirs = ' '.join(['-I' + env.GetBuildPath(d) for d in env['PROTOCPATH']])
|
||||||
|
return '$PROTOC $PROTOCFLAGS %s --decode=%s %s <%s >%s' % (dirs, env['MESSAGE'], source[1], source[0], target[0])
|
||||||
|
|
||||||
|
decode_builder = Builder(generator = decode_actions,
|
||||||
|
suffix = '.decoded')
|
||||||
|
env.Append(BUILDERS = {'Decode': decode_builder})
|
||||||
|
|
||||||
|
# Build command that asserts that two files be equal
|
||||||
|
def compare_files(target, source, 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])
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
print '\033[31m[FAIL]\033[0m Files differ: ' + str(source[0]) + ' and ' + str(source[1])
|
||||||
|
return 1
|
||||||
|
|
||||||
|
compare_builder = Builder(action = compare_files,
|
||||||
|
suffix = '.equal')
|
||||||
|
env.Append(BUILDERS = {'Compare': compare_builder})
|
||||||
|
|
||||||
|
# Build command that checks that each pattern in source2 is found in source1.
|
||||||
|
def match_files(target, source, 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
|
||||||
|
else:
|
||||||
|
print '\033[32m[ OK ]\033[0m All patterns found in ' + str(source[0])
|
||||||
|
return 0
|
||||||
|
|
||||||
|
match_builder = Builder(action = match_files, suffix = '.matched')
|
||||||
|
env.Append(BUILDERS = {'Match': match_builder})
|
||||||
|
|
||||||
|
|
||||||
7
tests/special_characters/SConscript
Normal file
7
tests/special_characters/SConscript
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Test that special characters in .proto filenames work.
|
||||||
|
|
||||||
|
Import('env')
|
||||||
|
|
||||||
|
env.Proto("funny-proto+name has.characters.proto")
|
||||||
|
env.Nanopb("funny-proto+name has.characters.pb.c", "funny-proto+name has.characters.pb")
|
||||||
|
env.Object("funny-proto+name has.characters.pb.c")
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/* Test decoding of extension fields. */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <pb_decode.h>
|
|
||||||
#include "alltypes.pb.h"
|
|
||||||
#include "extensions.pb.h"
|
|
||||||
|
|
||||||
#define TEST(x) if (!(x)) { \
|
|
||||||
printf("Test " #x " failed.\n"); \
|
|
||||||
return 2; \
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
uint8_t buffer[1024];
|
|
||||||
size_t count = fread(buffer, 1, sizeof(buffer), stdin);
|
|
||||||
pb_istream_t stream = pb_istream_from_buffer(buffer, count);
|
|
||||||
|
|
||||||
AllTypes alltypes = {};
|
|
||||||
|
|
||||||
int32_t extensionfield1;
|
|
||||||
pb_extension_t ext1 = {&AllTypes_extensionfield1, &extensionfield1, NULL};
|
|
||||||
alltypes.extensions = &ext1;
|
|
||||||
|
|
||||||
ExtensionMessage extensionfield2 = {};
|
|
||||||
pb_extension_t ext2 = {&ExtensionMessage_AllTypes_extensionfield2, &extensionfield2, NULL};
|
|
||||||
ext1.next = &ext2;
|
|
||||||
|
|
||||||
if (!pb_decode(&stream, AllTypes_fields, &alltypes))
|
|
||||||
{
|
|
||||||
printf("Parsing failed: %s\n", PB_GET_ERROR(&stream));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(extensionfield1 == 12345)
|
|
||||||
TEST(strcmp(extensionfield2.test1, "test") == 0)
|
|
||||||
TEST(extensionfield2.test2 == 54321)
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
/*
|
|
||||||
* Tests if still compile if typedefs are redfefined in STATIC_ASSERTS when
|
|
||||||
* proto file includes another poto file
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <pb_encode.h>
|
|
||||||
#include "callbacks2.pb.h"
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
|
|
||||||
Test Person7foobar@foobar.com"
|
|
||||||
555-12345678
|
|
||||||
Reference in New Issue
Block a user