├── .gitignore ├── .travis.before_install.sh ├── .travis.yml ├── CHANGES.md ├── LICENSE ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── include └── protobuf2json.h ├── m4 ├── .gitignore ├── acinclude.m4 ├── my_coverage.m4 ├── my_sanitize.m4 └── my_valgrind.m4 ├── src ├── .gitignore ├── Makefile.am ├── base64.h ├── bitmap.h └── protobuf2json.c └── test ├── .gitignore ├── Makefile.am ├── benchmark-dummy.c ├── benchmark-list.h ├── failed-alloc-helper.h ├── fixtures ├── bad_json.json ├── bad_message.json └── good.json ├── getrusage-helper.h ├── run-benchmarks.c ├── run-tests.c ├── run-tmp.c ├── runner-unix.c ├── runner-unix.h ├── runner-win.c ├── runner-win.h ├── runner.c ├── runner.h ├── task.h ├── test-json2protobuf-file.c ├── test-json2protobuf-string.c ├── test-list.h ├── test-protobuf2json-file.c ├── test-protobuf2json-string.c ├── test-reversible.c └── test.proto /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | # Libraries 6 | *.lib 7 | *.a 8 | 9 | # Shared objects (inc. Windows DLLs) 10 | *.dll 11 | *.so 12 | *.so.* 13 | *.dylib 14 | 15 | # Executables 16 | *.exe 17 | *.out 18 | *.app 19 | 20 | # Autotools 21 | /aclocal.m4 22 | /ar-lib 23 | /autom4te.cache/ 24 | /compile 25 | /config.guess 26 | /config.log 27 | /config.status 28 | /config.sub 29 | /configure 30 | /depcomp 31 | /install-sh 32 | /libtool 33 | /ltmain.sh 34 | /missing 35 | /test-driver 36 | Makefile 37 | Makefile.in 38 | 39 | # Dummy 40 | *.Plo 41 | 42 | # Coverage 43 | *.gcda 44 | *.gcno 45 | *.gcov 46 | /*-coverage.info 47 | /*-coverage/ 48 | -------------------------------------------------------------------------------- /.travis.before_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (c) 2014-2016 Oleg Efimov 4 | # 5 | # protobuf2json-c is free software; you can redistribute it 6 | # and/or modify it under the terms of the MIT license. 7 | # See LICENSE for details. 8 | 9 | echo TRAVIS_OS_NAME=$TRAVIS_OS_NAME 10 | 11 | PROTOBUF_VERSION=2.6.1 12 | echo PROTOBUF_VERSION=$PROTOBUF_VERSION 13 | 14 | PROTOBUF_C_VERSION=1.2.1 15 | echo PROTOBUF_C_VERSION=$PROTOBUF_C_VERSION 16 | 17 | echo "Installing protobuf..." 18 | wget https://github.com/google/protobuf/releases/download/v$PROTOBUF_VERSION/protobuf-$PROTOBUF_VERSION.tar.gz 19 | tar xf protobuf-$PROTOBUF_VERSION.tar.gz 20 | cd protobuf-$PROTOBUF_VERSION && ./configure && make -j2 && sudo make install && sudo ldconfig 21 | 22 | echo "Installing protobuf-c..." 23 | wget https://github.com/protobuf-c/protobuf-c/releases/download/v$PROTOBUF_C_VERSION/protobuf-c-$PROTOBUF_C_VERSION.tar.gz 24 | tar xf protobuf-c-$PROTOBUF_C_VERSION.tar.gz 25 | cd protobuf-c-$PROTOBUF_C_VERSION && ./configure && make -j2 && sudo make install && sudo ldconfig 26 | 27 | echo "Installing jansson and some tools..." 28 | if [ "x$TRAVIS_OS_NAME" = "xosx" ]; then 29 | brew update 30 | brew install jansson 31 | brew install cppcheck 32 | if [ "x$MY_VALGRIND" = "x1" ]; then brew install valgrind; fi 33 | else # linux 34 | sudo add-apt-repository --yes ppa:pi-rho/security 35 | sudo apt-get update 36 | sudo apt-get install libjansson4-dev libjansson4 37 | sudo apt-get install cppcheck 38 | if [ "x$MY_VALGRIND" = "x1" ]; then sudo apt-get install valgrind; fi 39 | if [ "x$MY_COVERAGE" = "x1" ]; then sudo apt-get install lcov; fi 40 | if [ "x$MY_COVERAGE" = "x1" ]; then sudo pip install cpp-coveralls; fi 41 | fi 42 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | - clang 5 | os: 6 | - linux 7 | - osx 8 | env: 9 | - 10 | - MY_SANITIZE=address 11 | - MY_SANITIZE=undefined 12 | - MY_SANITIZE=integer 13 | - MY_VALGRIND=1 14 | - MY_COVERAGE=1 15 | matrix: 16 | exclude: 17 | # Pairs because of some comilers does not support some features at all 18 | - compiler: gcc 19 | env: MY_SANITIZE=address 20 | - compiler: gcc 21 | env: MY_SANITIZE=undefined 22 | - compiler: gcc 23 | env: MY_SANITIZE=integer 24 | - compiler: clang 25 | env: MY_VALGRIND=1 26 | - compiler: clang 27 | env: MY_COVERAGE=1 28 | # OS-dependent features 29 | - os: osx 30 | compiler: clang 31 | env: MY_SANITIZE=address 32 | - os: osx 33 | compiler: clang 34 | env: MY_SANITIZE=undefined 35 | - os: osx 36 | compiler: clang 37 | env: MY_SANITIZE=integer 38 | - os: osx 39 | compiler: gcc 40 | env: MY_VALGRIND=1 41 | - os: osx 42 | compiler: gcc 43 | env: MY_COVERAGE=1 44 | before_install: 45 | - ./.travis.before_install.sh 46 | install: 47 | - ./autogen.sh 48 | - ./configure 49 | - if [ "x$MY_SANITIZE" != "x" ]; then ./configure --enable-my-sanitize=$MY_SANITIZE; fi 50 | - if [ "x$MY_VALGRIND" = "x1" ]; then ./configure --enable-my-valgrind; fi 51 | - if [ "x$MY_COVERAGE" = "x1" ]; then ./configure --enable-my-coverage; fi 52 | - make 53 | script: 54 | - make cppcheck 55 | - make test 56 | - make benchmark 57 | after_success: 58 | - if [ "x$MY_COVERAGE" = "x1" ]; then cpp-coveralls --build-root src --exclude test/ --exclude include/ --exclude src/.libs --gcov-options '\-lp'; fi 59 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | protobuf2json-c releases 2 | ======================== 3 | 4 | master - UNRELEASED 5 | ------------------- 6 | 7 | Nothing 8 | 9 | v0.4.0 - 28 Nov 2016 10 | -------------------- 11 | 12 | * New features 13 | 14 | - protobuf2json: bytes fields now encoded with base64 15 | 16 | v0.3.1 - 07 Feb 2016 17 | -------------------- 18 | 19 | * Fixes: 20 | 21 | - protobuf2json.h installation 22 | 23 | 24 | v0.3.0 - 07 Feb 2016 25 | -------------------- 26 | 27 | * New features 28 | 29 | - protobuf2json: support oneof 30 | 31 | * Fixes: 32 | 33 | - protobuf2json: few memory leaks fixed 34 | 35 | * Other 36 | 37 | - build: add Travis OS X tests 38 | 39 | 40 | v0.2.1 - 05 Jan 2015 41 | -------------------- 42 | 43 | * New features 44 | 45 | - protobuf2json: allow integers in JSON for float/double protobuf fields 46 | 47 | * Other 48 | 49 | - build: implement valgrind check 50 | - test: add tests for protobuf2json_file function fails 51 | - other: fix warnings that can be muted with -Wpointer-sign 52 | - test: fix coverage info generation on OS X 53 | - test: better coverage for repeated values support 54 | 55 | 56 | v0.2.0 - 14 Dec 2014 57 | -------------------- 58 | 59 | * Incompatible changes 60 | 61 | - build: remove jansson_compat.h, require latest jansson version (2.7) 62 | 63 | * New features: 64 | 65 | - protobuf2json: implement protobuf2json_file() function 66 | - protobuf2json: implement bytes type support 67 | - protobuf2json: implement presented required fields check 68 | 69 | * Fixes: 70 | 71 | - protobuf2json: fix null pointer dereference reported by cppcheck 72 | - protobuf2json: fix memory leaks reported by valgrind 73 | - test: cover more types mismatch error messages, correct some error codes 74 | 75 | * Other 76 | 77 | - docs: API documentation 78 | - build: specify -version-info for libtool 79 | - build: run cppcheck in builds 80 | - test: get rid of protobuf_c_default_allocator usage 81 | - build: remove allow_failures from Travis CI config 82 | - build: add coveralls support 83 | 84 | 85 | v0.1.1 - 04 May 2014 86 | -------------------- 87 | 88 | * Fixes: 89 | 90 | - protobuf2json: remove unnecessary json_decref 91 | - json2protobuf: fix boolean values converting 92 | - test: allocate test file path in heap 93 | 94 | * Other: 95 | 96 | - build: sanitizing integer errors 97 | - test: free json_string and protobuf_message 98 | - test: use TEST_JSON_FLAGS in all positive tests 99 | 100 | 101 | v0.1.0 - 02 May 2014 102 | -------------------- 103 | 104 | * New features: 105 | 106 | - json2protobuf: support for all numeric types and boolean 107 | 108 | * Other: 109 | 110 | - api: change error constants values 111 | 112 | 113 | v0.0.5 - 01 May 2014 114 | -------------------- 115 | 116 | * New features: 117 | 118 | - protobuf2json: add missed error string setting for unsupported field type 119 | 120 | * Other: 121 | 122 | - api: change error constants values 123 | - test: cover numeric types GPB -> JSON converting 124 | 125 | 126 | v0.0.4 - 29 Apr 2014 127 | -------------------- 128 | 129 | * New features: 130 | 131 | - protobuf2json: errors reporting, cover all memory and jansson errors 132 | 133 | * Other: 134 | 135 | - build: fix external build (#1) (Antony Dovgal) 136 | 137 | 138 | v0.0.3 - 15 Apr 2014 139 | -------------------- 140 | 141 | * New features: 142 | 143 | - json2protobuf: JSON parsing errors reporting 144 | - json2protobuf: calloc(3) errors reporting 145 | - json2protobuf: validate JSON structure while process message 146 | - json2protobuf: pass errors from nested messages processing 147 | 148 | * Other: 149 | 150 | - api: change error constants values 151 | - build: rename libprotobuf2json to libprotobuf2json-c 152 | 153 | 154 | v0.0.2 - 06 Apr 2014 155 | -------------------- 156 | 157 | * Initial version: 158 | 159 | - protobuf2json: define public API & simple messages support 160 | - json2protobuf: define public API & simple messages support 161 | - build: adapt libuv test runner 162 | - build: use autoconf 163 | - build: implement AX_SANITIZE macro 164 | - build: jansson_compat.h for old jansson 165 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 Oleg Efimov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014-2016 Oleg Efimov 2 | # 3 | # protobuf2json-c is free software; you can redistribute it 4 | # and/or modify it under the terms of the MIT license. 5 | # See LICENSE for details. 6 | 7 | ACLOCAL_AMFLAGS = -I m4 8 | 9 | SUBDIRS = src test 10 | 11 | cppcheck: 12 | cppcheck --enable=style src/protobuf2json.c include/protobuf2json.h --error-exitcode=2 13 | 14 | test: check 15 | @MY_VALGRIND_EXEC_PREFIX@./test/run-tests 16 | 17 | benchmark: check 18 | @MY_VALGRIND_EXEC_PREFIX@./test/run-benchmarks 19 | 20 | MY_COVERAGE_DIRECTORY = $(abs_top_builddir)/src 21 | @MY_COVERAGE_RULES@ 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | protobuf2json-c [![BS][BSI]][BSURL] [![CS][CSI]][CSURL] 2 | ======================================================= 3 | 4 | This is `protobuf2json-c`, pure C implementation of the [Google Protocol Buffers] to/from JSON converter. 5 | 6 | [BSI]: https://secure.travis-ci.org/Sannis/protobuf2json-c.png?branch=master 7 | [BSURL]: http://travis-ci.org/Sannis/protobuf2json-c 8 | 9 | [CSI]: https://coveralls.io/repos/Sannis/protobuf2json-c/badge.png 10 | [CSURL]: https://coveralls.io/r/Sannis/protobuf2json-c 11 | 12 | [Google Protocol Buffers]: https://developers.google.com/protocol-buffers/ 13 | 14 | Building 15 | -------- 16 | 17 | `protobuf2json-c` requires a C compiler, [protobuf-c][] 1.1.0 and [jansson][] 2.7 to be installed. 18 | If building from a git checkout, the `autotools` (`autoconf`, `automake`, `libtool`) must also be installed, 19 | and the build system must be generated by running the `autogen.sh` script: 20 | 21 | ./autogen.sh && ./configure && make && make install 22 | 23 | [protobuf-c]: https://github.com/protobuf-c/protobuf-c 24 | [jansson]: https://github.com/akheron/jansson 25 | 26 | API 27 | --- 28 | 29 | `protobuf2json-c` piblic API containt 6 functions: 30 | 3 for Protobuf to JSON conversion and 3 for JSON to Protobuf conversion. 31 | They uses `json_t` object, `char *` C-string or file for JSON. 32 | 33 | Protobuf to JSON conversion functions: 34 | 35 | ``` 36 | int protobuf2json_object( 37 | ProtobufCMessage *protobuf_message, 38 | json_t **json_object, 39 | char *error_string, 40 | size_t error_size 41 | ); 42 | ``` 43 | 44 | ``` 45 | int protobuf2json_string( 46 | ProtobufCMessage *protobuf_message, 47 | size_t json_flags, 48 | char **json_string, 49 | char *error_string, 50 | size_t error_size 51 | ); 52 | ``` 53 | 54 | ``` 55 | int protobuf2json_file( 56 | ProtobufCMessage *protobuf_message, 57 | size_t json_flags, 58 | char *json_file, 59 | char *fopen_mode, 60 | char *error_string, 61 | size_t error_size 62 | ); 63 | ``` 64 | 65 | JSON to Protobuf conversion functions: 66 | 67 | ``` 68 | int json2protobuf_object( 69 | json_t *json_object, 70 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 71 | ProtobufCMessage **protobuf_message, 72 | char *error_string, 73 | size_t error_size 74 | ); 75 | ``` 76 | 77 | ``` 78 | int json2protobuf_string( 79 | char *json_string, 80 | size_t json_flags, 81 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 82 | ProtobufCMessage **protobuf_message, 83 | char *error_string, 84 | size_t error_size 85 | ); 86 | ``` 87 | 88 | ``` 89 | int json2protobuf_file( 90 | char *json_file, 91 | size_t json_flags, 92 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 93 | ProtobufCMessage **protobuf_message, 94 | char *error_string, 95 | size_t error_size 96 | ); 97 | ``` 98 | 99 | Each of them have `error_string` and `error_size` arguments used to pass error description from `protobuf2json-c` functions. 100 | You can pass `NULL` and `0` to avoid setting error description. 101 | 102 | Credits 103 | ------- 104 | 105 | `protobuf2json-c` based on code from: 106 | - https://github.com/wickedck/protobuf-to-json 107 | - https://github.com/renenglish/pb2json 108 | - https://github.com/michaelstorm/Tangent 109 | 110 | License 111 | ------- 112 | 113 | MIT license. See license text in file [LICENSE](https://github.com/Sannis/protobuf2json-c/blob/master/LICENSE). 114 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (c) 2014-2016 Oleg Efimov 4 | # 5 | # protobuf2json-c is free software; you can redistribute it 6 | # and/or modify it under the terms of the MIT license. 7 | # See LICENSE for details. 8 | 9 | cd `dirname "$0"` 10 | 11 | if [ "$LIBTOOLIZE" = "" ] && [ "`uname`" = "Darwin" ]; then 12 | LIBTOOLIZE=glibtoolize 13 | fi 14 | 15 | ACLOCAL=${ACLOCAL:-aclocal} 16 | AUTOCONF=${AUTOCONF:-autoconf} 17 | AUTOMAKE=${AUTOMAKE:-automake} 18 | LIBTOOLIZE=${LIBTOOLIZE:-libtoolize} 19 | 20 | set -ex 21 | "$LIBTOOLIZE" 22 | "$ACLOCAL" -I m4 23 | "$AUTOCONF" 24 | "$AUTOMAKE" --add-missing --copy 25 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Copyright (c) 2014-2016 Oleg Efimov 2 | dnl 3 | dnl protobuf2json-c is free software; you can redistribute it 4 | dnl and/or modify it under the terms of the MIT license. 5 | dnl See LICENSE for details. 6 | 7 | AC_PREREQ([2.59]) 8 | AC_INIT([libprotobuf2json-c], [0.4.0], [efimovov@gmail.com]) 9 | AC_CONFIG_MACRO_DIR([m4]) 10 | 11 | AM_INIT_AUTOMAKE([foreign -Wall]) 12 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 13 | 14 | AC_ENABLE_SHARED 15 | AC_ENABLE_STATIC 16 | 17 | AC_PROG_CC 18 | AC_PROG_MAKE_SET 19 | 20 | AM_SILENT_RULES([yes]) 21 | 22 | LT_INIT 23 | 24 | AC_CHECK_LIB([pthread], [pthread_create]) 25 | AC_CHECK_LIB([pthread], [pthread_join]) 26 | 27 | AM_CONDITIONAL([WINNT], [AS_CASE([$host_os], [mingw*], [true], [false])]) 28 | 29 | AC_MSG_RESULT([]) 30 | 31 | AX_PROTOBUF_C 32 | AX_LIBJANSSON 33 | 34 | AC_MSG_RESULT([]) 35 | 36 | MY_SANITIZE 37 | MY_VALGRIND 38 | MY_COVERAGE 39 | 40 | AC_MSG_RESULT([]) 41 | 42 | AC_CONFIG_FILES([Makefile src/Makefile test/Makefile]) 43 | 44 | AC_OUTPUT 45 | -------------------------------------------------------------------------------- /include/protobuf2json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #ifndef PROTOBUF2JSON_H 10 | #define PROTOBUF2JSON_H 1 11 | 12 | #include 13 | #include 14 | 15 | /* Common errors */ 16 | #define PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY -001 17 | #define PROTOBUF2JSON_ERR_UNSUPPORTED_FIELD_TYPE -002 18 | #define PROTOBUF2JSON_ERR_UNKNOWN_ENUM_VALUE -003 19 | 20 | /* protobuf2json_string */ 21 | #define PROTOBUF2JSON_ERR_CANNOT_DUMP_STRING -101 22 | /* protobuf2json_file */ 23 | #define PROTOBUF2JSON_ERR_CANNOT_DUMP_FILE -102 24 | /* protobuf2json */ 25 | #define PROTOBUF2JSON_ERR_JANSSON_INTERNAL -201 26 | 27 | /*json2protobuf_string*/ 28 | #define PROTOBUF2JSON_ERR_CANNOT_PARSE_STRING -301 29 | /* json2protobuf_file */ 30 | #define PROTOBUF2JSON_ERR_CANNOT_PARSE_FILE -302 31 | /* json2protobuf */ 32 | #define PROTOBUF2JSON_ERR_UNKNOWN_FIELD -401 33 | #define PROTOBUF2JSON_ERR_IS_NOT_OBJECT -402 34 | #define PROTOBUF2JSON_ERR_IS_NOT_ARRAY -403 35 | #define PROTOBUF2JSON_ERR_IS_NOT_INTEGER -404 36 | #define PROTOBUF2JSON_ERR_IS_NOT_INTEGER_OR_REAL -405 37 | #define PROTOBUF2JSON_ERR_IS_NOT_BOOLEAN -406 38 | #define PROTOBUF2JSON_ERR_IS_NOT_STRING -407 39 | #define PROTOBUF2JSON_ERR_REQUIRED_IS_MISSING -408 40 | /*#define PROTOBUF2JSON_ERR_DUPLICATE_FIELD -???*/ 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | /* === Protobuf -> JSON === */ 47 | 48 | int protobuf2json_object( 49 | ProtobufCMessage *protobuf_message, 50 | json_t **json_object, 51 | char *error_string, 52 | size_t error_size 53 | ); 54 | 55 | int protobuf2json_string( 56 | ProtobufCMessage *protobuf_message, 57 | size_t json_flags, 58 | char **json_string, 59 | char *error_string, 60 | size_t error_size 61 | ); 62 | 63 | int protobuf2json_file( 64 | ProtobufCMessage *protobuf_message, 65 | size_t json_flags, 66 | char *json_file, 67 | char *fopen_mode, 68 | char *error_string, 69 | size_t error_size 70 | ); 71 | 72 | /* === JSON -> Protobuf === */ 73 | 74 | int json2protobuf_object( 75 | json_t *json_object, 76 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 77 | ProtobufCMessage **protobuf_message, 78 | char *error_string, 79 | size_t error_size 80 | ); 81 | 82 | int json2protobuf_string( 83 | char *json_string, 84 | size_t json_flags, 85 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 86 | ProtobufCMessage **protobuf_message, 87 | char *error_string, 88 | size_t error_size 89 | ); 90 | 91 | int json2protobuf_file( 92 | char *json_file, 93 | size_t json_flags, 94 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 95 | ProtobufCMessage **protobuf_message, 96 | char *error_string, 97 | size_t error_size 98 | ); 99 | 100 | /* === END === */ 101 | 102 | #ifdef __cplusplus 103 | } 104 | #endif 105 | 106 | #endif /* PROTOBUF2JSON_H */ 107 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore libtoolize-generated files. 2 | *.m4 3 | -------------------------------------------------------------------------------- /m4/acinclude.m4: -------------------------------------------------------------------------------- 1 | dnl Copyright (c) 2014-2016 Oleg Efimov 2 | dnl 3 | dnl protobuf2json-c is free software; you can redistribute it 4 | dnl and/or modify it under the terms of the MIT license. 5 | dnl See LICENSE for details. 6 | 7 | AC_DEFUN([AX_PROTOBUF_C], 8 | [ 9 | AC_ARG_WITH([protobuf_c], AC_HELP_STRING([--with-protobuf-c=DIR], [protobuf-c install directory])) 10 | 11 | PROTOBUF_C_INCLUDES="" 12 | PROTOBUF_C_CFLAGS="" 13 | PROTOBUF_C_LIBS="" 14 | PROTOBUF_C_COMPILER="" 15 | 16 | have_protobuf_c=false 17 | if test "$1" != "optional" -o \( "$with_protobuf_c" != "no" -a -n "$with_protobuf_c" \) ; then 18 | AC_MSG_CHECKING([for protobuf-c headers]) 19 | 20 | protobuf_c_path="" 21 | for path in "$with_protobuf_c" /usr/local /usr /opt/local /opt ; do 22 | if test -r "$path/include/google/protobuf-c/protobuf-c.h" -a "$path/bin/protoc-c" ; then 23 | protobuf_c_path=$path 24 | break 25 | fi 26 | done 27 | 28 | if test -n "$protobuf_c_path" ; then 29 | have_protobuf_c=true 30 | AC_MSG_RESULT([$protobuf_c_path]) 31 | PROTOBUF_C_INCLUDES="-I$protobuf_c_path/include" 32 | PROTOBUF_C_LIBS="-L$protobuf_c_path/lib -lprotobuf-c" 33 | PROTOBUF_C_CFLAGS="-DPRINT_UNPACK_ERRORS=0" 34 | PROTOBUF_C_COMPILER="$path/bin/protoc-c" 35 | else 36 | AC_MSG_ERROR([protobuf_c headers not found]) 37 | fi 38 | fi 39 | 40 | AM_CONDITIONAL([ENABLE_PROTOBUF_C], [test "x$have_protobuf_c" = "xtrue"]) 41 | 42 | if test "x$have_protobuf_c" = "xtrue" ; then 43 | AC_MSG_RESULT([protobuf_c: INCLUDES=$PROTOBUF_C_INCLUDES, LIBS=$PROTOBUF_C_LIBS, CFLAGS=$PROTOBUF_C_CFLAGS, PROTOC-C=$PROTOBUF_C_COMPILER]) 44 | fi 45 | 46 | AC_SUBST([PROTOBUF_C_INCLUDES]) 47 | AC_SUBST([PROTOBUF_C_LIBS]) 48 | AC_SUBST([PROTOBUF_C_CFLAGS]) 49 | AC_SUBST([PROTOBUF_C_COMPILER]) 50 | ]) 51 | 52 | AC_DEFUN([AX_LIBJANSSON], 53 | [ 54 | AC_ARG_WITH([libjansson], AC_HELP_STRING([--with-libjansson=DIR], [libjansson install directory])) 55 | 56 | LIBJANSSON_INCLUDES="" 57 | LIBJANSSON_LIBS="" 58 | 59 | have_libjansson=false 60 | if test "$1" != "optional" -o \( "$with_libjansson" != "no" -a -n "$with_libjansson" \) ; then 61 | AC_MSG_CHECKING([for libjansson headers]) 62 | 63 | libjansson_path="" 64 | for dir in "$with_libjansson" /usr/local /usr /opt/local /opt ; do 65 | if test -r "$dir/include/jansson.h" ; then 66 | libjansson_path="$dir" 67 | break 68 | fi 69 | done 70 | 71 | if test -n "$libjansson_path" ; then 72 | have_libjansson=true 73 | AC_MSG_RESULT([$libjansson_path]) 74 | LIBJANSSON_INCLUDES="-I$libjansson_path/include" 75 | LIBJANSSON_LIBS="-L$libjansson_path/lib -ljansson" 76 | else 77 | AC_MSG_ERROR([libjansson headers not found]) 78 | fi 79 | fi 80 | 81 | AM_CONDITIONAL([ENABLE_LIBJANSSON], [test "x$have_libjansson" = "xtrue"]) 82 | 83 | if test "x$have_libjansson" = "xtrue" ; then 84 | AC_MSG_RESULT([libjansson: INCLUDES=$LIBJANSSON_INCLUDES, LIBS=$LIBJANSSON_LIBS]) 85 | fi 86 | 87 | AC_SUBST([LIBJANSSON_INCLUDES]) 88 | AC_SUBST([LIBJANSSON_LIBS]) 89 | ]) 90 | -------------------------------------------------------------------------------- /m4/my_coverage.m4: -------------------------------------------------------------------------------- 1 | # SYNOPSIS 2 | # 3 | # MY_COVERAGE() 4 | # 5 | # DESCRIPTION 6 | # 7 | # Defines MY_COVERAGE_CFLAGS, MY_COVERAGE_CXXFLAGS and MY_COVERAGE_LDFLAGS which should be 8 | # included in the CFLAGS, CXXFLAGS and LIBS/LDFLAGS variables of every build target 9 | # (program or library) which should be built with code coverage support. 10 | # Also defines MY_COVERAGE_RULES which should be substituted in your 11 | # Makefile.am; and $enable_code_coverage which can be used in subsequent 12 | # configure output. MY_COVERAGE_ENABLED is defined and substituted, and 13 | # corresponds to the value of the --enable-my-coverage option, which 14 | # defaults to being disabled. 15 | # 16 | # Note that all optimisation flags in CFLAGS/CXXFLAGS defined before MY_COVERAGE execution 17 | # will be stripped when code coverage is enabled. 18 | # 19 | # Usage example: 20 | # configure.ac: 21 | # MY_COVERAGE 22 | # 23 | # Makefile.am: 24 | # @MY_COVERAGE_RULES@ 25 | # my_program_LIBS = … $(MY_COVERAGE_LDFLAGS) … 26 | # my_program_CFLAGS = … $(MY_COVERAGE_CFLAGS) … 27 | # my_program_CXXFLAGS = … $(MY_COVERAGE_CXXFLAGS) … 28 | # 29 | # This results in a “coverage” rule being added to any Makefile.am which includes “@MY_COVERAGE_RULES@” 30 | # (assuming the module has been configured with --enable-my-coverage). Running `make coverage` 31 | # in that directory generate a code coverage report detailing the code which was touched, 32 | # then print the URI for the report. 33 | # 34 | # CREDITS 35 | # 36 | # MY_COVERAGE is mostly based on the AX_COVERAGE / GNOME_COVERAGE macro: 37 | # 38 | # http://savannah.gnu.org/patch/?8451 39 | # 40 | # with some bits from protobuf-c version: 41 | # 42 | # https://github.com/protobuf-c/protobuf-c/blob/e72e7e7e81d75206f54918c5270e334bd2c25fd6/m4/code_coverage.m4 43 | # 44 | # LICENSE 45 | # 46 | # Copyright © 2012, 2014 Philip Withnall 47 | # Copyright © 2012 Xan Lopez 48 | # Copyright © 2012 Christian Persch 49 | # Copyright © 2012 Paolo Borelli 50 | # Copyright © 2012 Dan Winship 51 | # Copyright © 2014 Robert Edmonds 52 | # Copyright © 2014 Oleg Efimov 53 | # 54 | # This file is licenced under MIT license. 55 | 56 | AC_DEFUN([MY_COVERAGE], 57 | [ 58 | dnl Check for --enable-my-coverage 59 | AC_MSG_CHECKING([whether to build with code coverage]) 60 | AC_ARG_ENABLE( 61 | [my-coverage], 62 | AS_HELP_STRING([--enable-my-coverage], [enable code coverage]), 63 | enable_my_coverage=yes, 64 | enable_my_coverage=no 65 | ) 66 | AC_MSG_RESULT($enable_my_coverage) 67 | 68 | AM_CONDITIONAL([MY_COVERAGE_ENABLED], [test "x$enable_my_coverage" = "xyes"]) 69 | AC_SUBST([MY_COVERAGE_ENABLED], [$enable_my_coverage]) 70 | 71 | dnl Do extra checks for enabled code coverage 72 | if test "x$enable_my_coverage" = "xyes"; then 73 | dnl We need gcc 74 | if test "x$GCC" != "xyes"; then 75 | AC_MSG_ERROR([GCC is required for --enable-my-coverage]) 76 | fi 77 | 78 | dnl Check if ccache is being used 79 | AC_CHECK_PROG(SHTOOL, shtool, shtool) 80 | if test "$SHTOOL"; then 81 | AS_CASE( 82 | [`$SHTOOL path $CC`], 83 | [*ccache*], 84 | [ccache=yes], 85 | [ccache=no] 86 | ) 87 | fi 88 | 89 | if test "x$ccache" = "xyes" && (test -z "$CCACHE_DISABLE" || test "$CCACHE_DISABLE" != "1"); then 90 | AC_MSG_ERROR([ccache must be disabled when --enable-my-coverage option is used. You can disable ccache by setting environment variable CCACHE_DISABLE=1.]) 91 | fi 92 | 93 | AC_CHECK_PROG(GCOV, gcov, gcov) 94 | if test -z "$GCOV"; then 95 | AC_MSG_ERROR([Could not find gcov from the gcc package]) 96 | fi 97 | 98 | AC_CHECK_PROG(LCOV, lcov, lcov) 99 | if test -z "$LCOV"; then 100 | AC_MSG_ERROR([Could not find lcov from the lcov package]) 101 | fi 102 | 103 | AC_CHECK_PROG(GENHTML, genhtml, genhtml) 104 | if test -z "$GENHTML"; then 105 | AC_MSG_ERROR([Could not find genhtml from the lcov package]) 106 | fi 107 | 108 | dnl Remove all optimization flags from compiler flags 109 | changequote({,}) 110 | CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'` 111 | CXXFLAGS=`echo "$CXXFLAGS" | $SED -e 's/-O[0-9]*//g'` 112 | changequote([,]) 113 | 114 | dnl Add the special compiler flags 115 | MY_COVERAGE_CFLAGS="--coverage -g -O0" 116 | MY_COVERAGE_CXXFLAGS="--coverage -g -O0" 117 | MY_COVERAGE_LDFLAGS="--coverage" 118 | fi 119 | 120 | AC_SUBST(MY_COVERAGE_CFLAGS) 121 | AC_SUBST(MY_COVERAGE_CXXFLAGS) 122 | AC_SUBST(MY_COVERAGE_LDFLAGS) 123 | 124 | MY_COVERAGE_RULES=' 125 | # {{{ MY_COVERAGE_RULES 126 | # 127 | # Code coverage 128 | # 129 | # Optional: 130 | # - MY_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. 131 | # (Default: $(abs_top_builddir)) 132 | # - MY_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated 133 | # by lcov for code coverage. (Default: 134 | # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) 135 | # - MY_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage 136 | # reports to be created. (Default: 137 | # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) 138 | # - MY_COVERAGE_LCOV_OPTIONS: Extra options to pass to the lcov instance. 139 | # (Default: empty) 140 | # - MY_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml 141 | # instance. (Default: empty) 142 | # - MY_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore. 143 | # (Default: empty) 144 | # 145 | # The generated report will be titled using the $(PACKAGE_NAME) and 146 | # $(PACKAGE_VERSION). 147 | 148 | # Optional variables 149 | MY_COVERAGE_DIRECTORY ?= $(abs_top_builddir) 150 | MY_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info 151 | MY_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage 152 | MY_COVERAGE_LCOV_OPTIONS ?= 153 | MY_COVERAGE_GENHTML_OPTIONS ?= 154 | MY_COVERAGE_IGNORE_PATTERN ?= 155 | 156 | # Some additional lcov options 157 | MY_COVERAGE_LCOV_OPTIONS += --no-external --no-checksum --compat-libtool 158 | 159 | MY_V_COV = $(my_v_COV_$(V)) 160 | my_v_COV_ = $(my_v_COV_$(AM_DEFAULT_VERBOSITY)) 161 | my_v_COV_0 = @echo " COV " $$@@; 162 | my_v_COV_1 = 163 | 164 | MY_COVERAGE_QUIET = $(my_coverage_quiet_$(V)) 165 | my_coverage_quiet_ = $(my_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) 166 | my_coverage_quiet_0 = --quiet 167 | my_coverage_quiet_1 = 168 | 169 | # Rules 170 | 171 | clean-gcno: 172 | @echo Removing old coverage sources 173 | -find . -name '*.gcno' -delete 174 | 175 | clean-gcda: 176 | @echo Removing old coverage results 177 | -find . -name '*.gcda' -delete 178 | 179 | clean-gcov: 180 | @echo Removing old coverage sources 181 | -find . -name '*.gcov' -delete 182 | 183 | .PHONY: clean-gcno clean-gcda clean-gcov 184 | 185 | $(MY_COVERAGE_OUTPUT_FILE).tmp: 186 | ifeq ($(MY_COVERAGE_ENABLED),yes) 187 | $(MY_V_COV)$(LCOV) $(MY_COVERAGE_QUIET) --directory $(MY_COVERAGE_DIRECTORY) --capture --output-file $(MY_COVERAGE_OUTPUT_FILE).tmp $(MY_COVERAGE_LCOV_OPTIONS) 188 | else 189 | $(error "Need to reconfigure with --enable-my-coverage") 190 | endif 191 | 192 | $(MY_COVERAGE_OUTPUT_FILE): $(MY_COVERAGE_OUTPUT_FILE).tmp 193 | ifeq ($(MY_COVERAGE_ENABLED),yes) 194 | $(MY_V_COV)$(LCOV) $(MY_COVERAGE_QUIET) --directory $(MY_COVERAGE_DIRECTORY) --remove $(MY_COVERAGE_OUTPUT_FILE).tmp $(MY_COVERAGE_IGNORE_PATTERN) --output-file $(MY_COVERAGE_OUTPUT_FILE) 195 | -@rm -f $(MY_COVERAGE_OUTPUT_FILE).tmp 196 | else 197 | $(error "Need to reconfigure with --enable-my-coverage") 198 | endif 199 | 200 | $(MY_COVERAGE_OUTPUT_DIRECTORY): $(MY_COVERAGE_OUTPUT_FILE) 201 | ifeq ($(MY_COVERAGE_ENABLED),yes) 202 | $(MY_V_COV)LANG=C $(GENHTML) $(MY_COVERAGE_QUIET) --prefix $(MY_COVERAGE_DIRECTORY) --output-directory $(MY_COVERAGE_OUTPUT_DIRECTORY) --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details $(MY_COVERAGE_OUTPUT_FILE) $(MY_COVERAGE_GENHTML_OPTIONS) 203 | else 204 | $(error "Need to reconfigure with --enable-my-coverage") 205 | endif 206 | 207 | coverage: $(MY_COVERAGE_OUTPUT_DIRECTORY) 208 | ifeq ($(MY_COVERAGE_ENABLED),yes) 209 | $(MY_V_COV)$(LCOV) --summary $(MY_COVERAGE_OUTPUT_FILE) 210 | @echo "file://$(abs_builddir)/$(MY_COVERAGE_OUTPUT_DIRECTORY)/index.html" 211 | else 212 | $(error "Need to reconfigure with --enable-my-coverage") 213 | endif 214 | 215 | clean-coverage: clean-gcda clean-gcov 216 | $(LCOV) --directory $(MY_COVERAGE_DIRECTORY) -z 217 | rm -rf $(MY_COVERAGE_OUTPUT_FILE) $(MY_COVERAGE_OUTPUT_FILE).tmp $(MY_COVERAGE_OUTPUT_DIRECTORY) 218 | 219 | .PHONY: coverage clean-coverage 220 | 221 | clean-local: clean-coverage 222 | 223 | distclean-local: clean-gcno 224 | 225 | # }}} MY_COVERAGE_RULES 226 | ' 227 | 228 | AC_SUBST([MY_COVERAGE_RULES]) 229 | m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([MY_COVERAGE_RULES])]) 230 | 231 | ]) # MY_COVERAGE 232 | 233 | -------------------------------------------------------------------------------- /m4/my_sanitize.m4: -------------------------------------------------------------------------------- 1 | dnl Copyright (c) 2014-2016 Oleg Efimov 2 | dnl 3 | dnl protobuf2json-c is free software; you can redistribute it 4 | dnl and/or modify it under the terms of the MIT license. 5 | dnl See LICENSE for details. 6 | 7 | AC_DEFUN([MY_SANITIZE], 8 | [ 9 | dnl Check for --enable-my-sanitize 10 | AC_MSG_CHECKING([whether to build with sanitizers]) 11 | AC_ARG_ENABLE( 12 | [my-sanitize], 13 | AS_HELP_STRING( 14 | [--enable-my-sanitize=SANITIZE], 15 | [enable -fsanitize=SANITIZE with some extra flags, SANITIZE should be one of: address, undefined, integer, memory or thread] 16 | ), 17 | enable_my_sanitize=$enableval, 18 | enable_my_sanitize=no 19 | ) 20 | AC_MSG_RESULT($enable_my_sanitize) 21 | 22 | MY_SANITIZE_CFLAGS="" 23 | MY_SANITIZE_LIBS="" 24 | 25 | # AddressSanitizer 26 | if test "x$enable_my_sanitize" = "xaddress" ; then 27 | MY_SANITIZE_CFLAGS+="-fsanitize=address" 28 | MY_SANITIZE_LIBS+="-fsanitize=address" 29 | 30 | AC_MSG_RESULT([sanitize: -fsanitize=address]) 31 | fi 32 | 33 | # -fsanitize=undefined 34 | if test "x$enable_my_sanitize" = "xundefined" ; then 35 | MY_SANITIZE_CFLAGS+="-fsanitize=undefined" 36 | MY_SANITIZE_LIBS+="-fsanitize=undefined" 37 | 38 | AC_MSG_RESULT([sanitize: -fsanitize=undefined]) 39 | fi 40 | 41 | # -fsanitize=integer 42 | # -fsanitize=integer-divide-by-zero 43 | # -fsanitize=signed-integer-overflow 44 | if test "x$enable_my_sanitize" = "xinteger" ; then 45 | MY_SANITIZE_CFLAGS+="-fsanitize=integer -fsanitize=integer-divide-by-zero -fsanitize=signed-integer-overflow" 46 | MY_SANITIZE_LIBS+="-fsanitize=integer -fsanitize=integer-divide-by-zero -fsanitize=signed-integer-overflow" 47 | 48 | AC_MSG_RESULT([sanitize: -fsanitize=integer -fsanitize=integer-divide-by-zero -fsanitize=signed-integer-overflow]) 49 | fi 50 | 51 | # MemorySanitizer 52 | # MemorySanitizer requires that all program code is instrumented. 53 | # This also includes any libraries that the program depends on, even libc. 54 | # Failing to achieve this may result in false reports. 55 | if test "x$enable_my_sanitize" = "xmemory" ; then 56 | MY_SANITIZE_CFLAGS+="-fsanitize=memory -fsanitize-memory-track-origins" 57 | MY_SANITIZE_LIBS+="-fsanitize=memory -fsanitize-memory-track-origins" 58 | 59 | AC_MSG_RESULT([sanitize: -fsanitize=memory -fsanitize-memory-track-origins]) 60 | fi 61 | 62 | # ThreadSanitizer 63 | if test "x$enable_my_sanitize" = "xthread" ; then 64 | MY_SANITIZE_CFLAGS+="-fsanitize=thread" 65 | MY_SANITIZE_LIBS+="-fsanitize=thread" 66 | 67 | AC_MSG_RESULT([sanitize: -fsanitize=thread]) 68 | fi 69 | 70 | if test "x$enable_my_sanitize" != "xno" ; then 71 | if test "x$MY_SANITIZE_CFLAGS" = "x"; then 72 | AC_MSG_ERROR([sanitize: unknown option '$enable_my_sanitize']) 73 | else 74 | # To get a reasonable performance add -O1 or higher. 75 | # Use -g to get file names and line numbers in the warning messages. 76 | MY_SANITIZE_CFLAGS+=" -g -O1" 77 | MY_SANITIZE_LIBS+=" -g" 78 | 79 | # To get meaningful stack traces in error messages add -fno-omit-frame-pointer. 80 | # To get perfect stack traces you may need to disable inlining (just use -O1) 81 | # and tail call elimination (-fno-optimize-sibling-calls). 82 | MY_SANITIZE_CFLAGS+=" -fno-omit-frame-pointer -fno-optimize-sibling-calls" 83 | fi 84 | fi 85 | 86 | AC_SUBST([MY_SANITIZE_CFLAGS]) 87 | AC_SUBST([MY_SANITIZE_LIBS]) 88 | ]) 89 | -------------------------------------------------------------------------------- /m4/my_valgrind.m4: -------------------------------------------------------------------------------- 1 | dnl Copyright (c) 2014-2016 Oleg Efimov 2 | dnl 3 | dnl protobuf2json-c is free software; you can redistribute it 4 | dnl and/or modify it under the terms of the MIT license. 5 | dnl See LICENSE for details. 6 | 7 | AC_DEFUN([MY_VALGRIND], 8 | [ 9 | dnl Check for --enable-my-valgrind 10 | AC_MSG_CHECKING([whether to build with valgrind checks]) 11 | AC_ARG_ENABLE( 12 | [my-valgrind], 13 | AS_HELP_STRING([--enable-my-valgrind], [enable valgrind checks]), 14 | enable_my_valgrind=yes, 15 | enable_my_valgrind=no 16 | ) 17 | AC_MSG_RESULT($enable_my_valgrind) 18 | 19 | MY_VALGRIND_CFLAGS="" 20 | MY_VALGRIND_LIBS="" 21 | MY_VALGRIND_EXEC_PREFIX="" 22 | 23 | if test "x$enable_my_valgrind" = "xyes"; then 24 | # To get a reasonable performance add -O1 or higher. 25 | # Use -g to get file names and line numbers in the warning messages. 26 | MY_VALGRIND_CFLAGS+=" -g -O1" 27 | MY_VALGRIND_LIBS+=" -g" 28 | 29 | MY_VALGRIND_EXEC_PREFIX="valgrind --leak-check=full --leak-resolution=med --trace-children=yes " 30 | 31 | AC_MSG_RESULT([valgrind: $MY_VALGRIND_EXEC_PREFIX]) 32 | fi 33 | 34 | AC_SUBST([MY_VALGRIND_CFLAGS]) 35 | AC_SUBST([MY_VALGRIND_LIBS]) 36 | AC_SUBST([MY_VALGRIND_EXEC_PREFIX]) 37 | ]) 38 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | .deps 4 | .libs 5 | *.o 6 | *.lo 7 | *.la 8 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014-2016 Oleg Efimov 2 | # 3 | # protobuf2json-c is free software; you can redistribute it 4 | # and/or modify it under the terms of the MIT license. 5 | # See LICENSE for details. 6 | 7 | lib_LTLIBRARIES = libprotobuf2json-c.la 8 | 9 | libprotobuf2json_c_la_SOURCES = protobuf2json.c 10 | 11 | # 1. Programs using the previous version may use the new version as drop-in replacement, 12 | # and programs using the new version can also work with the previous one. 13 | # In other words, no recompiling nor relinking is needed. 14 | # In this case, bump revision only, don’t touch current nor age. 15 | # 2. Programs using the previous version may use the new version as drop-in replacement, 16 | # but programs using the new version may use APIs not present in the previous one. 17 | # In other words, a program linking against the new version may fail with “unresolved symbols” 18 | # if linking against the old version at runtime: set revision to 0, bump current and age. 19 | # 3. Programs may need to be changed, recompiled, and relinked in order to use the new version. 20 | # Bump current, set revision and age to 0. 21 | # 22 | # current[:revision[:age]] 23 | libprotobuf2json_c_la_LDFLAGS = -version-info 3:0:0 24 | 25 | include_HEADERS = ../include/protobuf2json.h 26 | 27 | AM_CFLAGS = -I$(top_srcdir)/include 28 | libprotobuf2json_c_la_LIBADD = 29 | 30 | AM_CFLAGS += $(PROTOBUF_C_INCLUDES) 31 | libprotobuf2json_c_la_LIBADD += $(PROTOBUF_C_LIBS) 32 | AM_CFLAGS += $(PROTOBUF_C_CFLAGS) 33 | 34 | AM_CFLAGS += $(LIBJANSSON_INCLUDES) 35 | libprotobuf2json_c_la_LIBADD += $(LIBJANSSON_LIBS) 36 | 37 | AM_CFLAGS += $(MY_SANITIZE_CFLAGS) 38 | libprotobuf2json_c_la_LIBADD += $(MY_SANITIZE_LIBS) 39 | 40 | AM_CFLAGS += $(MY_VALGRIND_CFLAGS) 41 | libprotobuf2json_c_la_LIBADD += $(MY_VALGRIND_LDFLAGS) 42 | 43 | AM_CFLAGS += $(MY_COVERAGE_CFLAGS) 44 | libprotobuf2json_c_la_LIBADD += $(MY_COVERAGE_LDFLAGS) 45 | -------------------------------------------------------------------------------- /src/base64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * Code based on nginx 1.5.5, extracted by Anton Povarov 5 | * 6 | * protobuf2json-c is free software; you can redistribute it 7 | * and/or modify it under the terms of the MIT license. 8 | * See LICENSE for details. 9 | */ 10 | 11 | #ifndef BASE64_H 12 | #define BASE64_H 1 13 | 14 | #define base64_encoded_len(len) (((len + 2) / 3) * 4) 15 | #define base64_decoded_len(len) (((len + 3) / 4) * 3) 16 | 17 | typedef struct base64_tables { 18 | unsigned char encode[64]; 19 | unsigned char decode[256]; 20 | unsigned char padding; 21 | } base64_tables_t; 22 | 23 | static const base64_tables_t base64_default_tables = { 24 | .encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 25 | .decode = { 26 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 27 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 28 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, 29 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, 30 | 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77, 32 | 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 33 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, 34 | 35 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 36 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 37 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 38 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 39 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 40 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 41 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 42 | 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 43 | }, 44 | .padding = '=' 45 | }; 46 | 47 | size_t base64_encode(char *dst, const char *src, size_t src_len) 48 | { 49 | char *d = dst; 50 | const unsigned char *s = (void*)src; 51 | const unsigned char *basis64 = base64_default_tables.encode; 52 | 53 | while (src_len > 2) { 54 | *d++ = basis64[(s[0] >> 2) & 0x3f]; 55 | *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)]; 56 | *d++ = basis64[((s[1] & 0x0f) << 2) | (s[2] >> 6)]; 57 | *d++ = basis64[s[2] & 0x3f]; 58 | 59 | s += 3; 60 | src_len -= 3; 61 | } 62 | 63 | if (src_len) { 64 | *d++ = basis64[(s[0] >> 2) & 0x3f]; 65 | 66 | if (src_len == 1) { 67 | *d++ = basis64[(s[0] & 3) << 4]; 68 | *d++ = base64_default_tables.padding; 69 | } else { 70 | *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)]; 71 | *d++ = basis64[(s[1] & 0x0f) << 2]; 72 | } 73 | 74 | *d++ = base64_default_tables.padding; 75 | } 76 | 77 | return (d - dst); 78 | } 79 | 80 | size_t base64_decode(char *dst, const char *src, size_t src_len) 81 | { 82 | size_t len; 83 | char *d = dst; 84 | const unsigned char *s = (void*)src; 85 | const unsigned char *basis = base64_default_tables.decode; 86 | 87 | for (len = 0; len < src_len; len++) { 88 | if (s[len] == base64_default_tables.padding) { 89 | break; 90 | } 91 | 92 | if (basis[s[len]] == 77) { 93 | return 0; 94 | } 95 | } 96 | 97 | if (len % 4 == 1) { 98 | return 0; 99 | } 100 | 101 | while (len > 3) { 102 | *d++ = (char) (basis[s[0]] << 2 | basis[s[1]] >> 4); 103 | *d++ = (char) (basis[s[1]] << 4 | basis[s[2]] >> 2); 104 | *d++ = (char) (basis[s[2]] << 6 | basis[s[3]]); 105 | 106 | s += 4; 107 | len -= 4; 108 | } 109 | 110 | if (len > 1) { 111 | *d++ = (char) (basis[s[0]] << 2 | basis[s[1]] >> 4); 112 | } 113 | 114 | if (len > 2) { 115 | *d++ = (char) (basis[s[1]] << 4 | basis[s[2]] >> 2); 116 | } 117 | 118 | return (d - dst); 119 | } 120 | 121 | #endif /* BASE64_H */ 122 | -------------------------------------------------------------------------------- /src/bitmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #ifndef BITMAP_H 10 | #define BITMAP_H 1 11 | 12 | typedef unsigned char bitmap_word_t; 13 | typedef bitmap_word_t* bitmap_t; 14 | 15 | #define bitmap_word_t_bits (8 * sizeof(bitmap_word_t)) 16 | #define bitmap_words_needed(size) (((size) + (bitmap_word_t_bits - 1)) / bitmap_word_t_bits) 17 | 18 | bitmap_t bitmap_alloc(int size) 19 | { 20 | return (bitmap_t)calloc(bitmap_words_needed(size), sizeof(bitmap_word_t)); 21 | } 22 | 23 | void bitmap_free(bitmap_t bitmap) 24 | { 25 | free(bitmap); 26 | } 27 | 28 | void bitmap_set(bitmap_t bitmap, unsigned int i) { 29 | bitmap[i / bitmap_word_t_bits] |= (1 << (i & (bitmap_word_t_bits - 1))); 30 | } 31 | 32 | int bitmap_get(bitmap_t bitmap, unsigned int i) { 33 | return (bitmap[i / bitmap_word_t_bits] & (1 << (i & (bitmap_word_t_bits - 1)))) ? 1 : 0; 34 | } 35 | 36 | #endif /* BITMAP_H */ 37 | -------------------------------------------------------------------------------- /src/protobuf2json.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* Interface definitions */ 15 | #include "protobuf2json.h" 16 | 17 | /* Simple bitmap implementation */ 18 | #include "bitmap.h" 19 | 20 | /* Simple base64 implementation */ 21 | #include "base64.h" 22 | 23 | /* === Defines === obviously private === */ 24 | 25 | #define SET_ERROR_STRING_AND_RETURN(error, error_string_format, ...) \ 26 | do { \ 27 | if (error_string && error_size) { \ 28 | snprintf( \ 29 | error_string, error_size, \ 30 | error_string_format, \ 31 | ##__VA_ARGS__ \ 32 | ); \ 33 | } \ 34 | return error; \ 35 | } while (0) 36 | 37 | /* === Protobuf -> JSON === Private === */ 38 | 39 | static size_t protobuf2json_value_size_by_type(ProtobufCType type) { 40 | switch (type) { 41 | case PROTOBUF_C_TYPE_INT32: 42 | case PROTOBUF_C_TYPE_SINT32: 43 | case PROTOBUF_C_TYPE_SFIXED32: 44 | case PROTOBUF_C_TYPE_UINT32: 45 | case PROTOBUF_C_TYPE_FIXED32: 46 | return 4; 47 | case PROTOBUF_C_TYPE_INT64: 48 | case PROTOBUF_C_TYPE_SINT64: 49 | case PROTOBUF_C_TYPE_SFIXED64: 50 | case PROTOBUF_C_TYPE_UINT64: 51 | case PROTOBUF_C_TYPE_FIXED64: 52 | return 8; 53 | case PROTOBUF_C_TYPE_FLOAT: 54 | return 4; 55 | case PROTOBUF_C_TYPE_DOUBLE: 56 | return 8; 57 | case PROTOBUF_C_TYPE_BOOL: 58 | return sizeof(protobuf_c_boolean); 59 | case PROTOBUF_C_TYPE_ENUM: 60 | return 4; 61 | case PROTOBUF_C_TYPE_STRING: 62 | return sizeof(char *); 63 | case PROTOBUF_C_TYPE_BYTES: 64 | return sizeof(ProtobufCBinaryData); 65 | case PROTOBUF_C_TYPE_MESSAGE: 66 | return sizeof(ProtobufCMessage *); 67 | default: 68 | assert(0); 69 | return 0; 70 | } 71 | } 72 | 73 | static int protobuf2json_process_message( 74 | const ProtobufCMessage *protobuf_message, 75 | json_t **json_message, 76 | char *error_string, 77 | size_t error_size 78 | ); 79 | 80 | static int protobuf2json_process_field( 81 | const ProtobufCFieldDescriptor *field_descriptor, 82 | const void *protobuf_value, 83 | json_t **json_value, 84 | char *error_string, 85 | size_t error_size 86 | ) { 87 | switch (field_descriptor->type) { 88 | case PROTOBUF_C_TYPE_INT32: 89 | case PROTOBUF_C_TYPE_SINT32: 90 | case PROTOBUF_C_TYPE_SFIXED32: 91 | *json_value = json_integer(*(int32_t *)protobuf_value); 92 | break; 93 | case PROTOBUF_C_TYPE_UINT32: 94 | case PROTOBUF_C_TYPE_FIXED32: 95 | *json_value = json_integer(*(uint32_t *)protobuf_value); 96 | break; 97 | case PROTOBUF_C_TYPE_INT64: 98 | case PROTOBUF_C_TYPE_SINT64: 99 | case PROTOBUF_C_TYPE_SFIXED64: 100 | *json_value = json_integer(*(int64_t *)protobuf_value); 101 | break; 102 | case PROTOBUF_C_TYPE_UINT64: 103 | case PROTOBUF_C_TYPE_FIXED64: 104 | *json_value = json_integer(*(uint64_t *)protobuf_value); 105 | break; 106 | case PROTOBUF_C_TYPE_FLOAT: 107 | *json_value = json_real(*(float *)protobuf_value); 108 | break; 109 | case PROTOBUF_C_TYPE_DOUBLE: 110 | *json_value = json_real(*(double *)protobuf_value); 111 | break; 112 | case PROTOBUF_C_TYPE_BOOL: 113 | *json_value = json_boolean(*(protobuf_c_boolean *)protobuf_value); 114 | break; 115 | case PROTOBUF_C_TYPE_ENUM: { 116 | const ProtobufCEnumValue *protobuf_enum_value = protobuf_c_enum_descriptor_get_value( 117 | field_descriptor->descriptor, 118 | *(int *)protobuf_value 119 | ); 120 | 121 | if (!protobuf_enum_value) { 122 | SET_ERROR_STRING_AND_RETURN( 123 | PROTOBUF2JSON_ERR_UNKNOWN_ENUM_VALUE, 124 | "Unknown value %d for enum '%s'", 125 | *(int *)protobuf_value, ((ProtobufCEnumDescriptor *)field_descriptor->descriptor)->name 126 | ); 127 | } 128 | 129 | *json_value = json_string((char *)protobuf_enum_value->name); 130 | 131 | break; 132 | } 133 | case PROTOBUF_C_TYPE_STRING: 134 | *json_value = json_string(*(char **)protobuf_value); 135 | break; 136 | case PROTOBUF_C_TYPE_BYTES: { 137 | const ProtobufCBinaryData *protobuf_binary = (const ProtobufCBinaryData *)protobuf_value; 138 | 139 | int base64_encoded_length = base64_encoded_len(protobuf_binary->len); 140 | 141 | char* base64_encoded_data = calloc(base64_encoded_length, sizeof(char)); 142 | if (!base64_encoded_data) { 143 | SET_ERROR_STRING_AND_RETURN( 144 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 145 | "Cannot allocate %zu bytes using calloc(3)", 146 | base64_encoded_length * sizeof(char) 147 | ); 148 | } 149 | 150 | base64_encoded_length = base64_encode(base64_encoded_data, (const char *)protobuf_binary->data, protobuf_binary->len); 151 | 152 | *json_value = json_stringn((const char *)base64_encoded_data, base64_encoded_length); 153 | 154 | free(base64_encoded_data); 155 | 156 | break; 157 | } 158 | case PROTOBUF_C_TYPE_MESSAGE: { 159 | const ProtobufCMessage **protobuf_message = (const ProtobufCMessage **)protobuf_value; 160 | 161 | int result = protobuf2json_process_message(*protobuf_message, json_value, error_string, error_size); 162 | if (result) { 163 | return result; 164 | } 165 | 166 | break; 167 | } 168 | default: 169 | assert(0); 170 | } 171 | 172 | if (!*json_value) { 173 | SET_ERROR_STRING_AND_RETURN( 174 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 175 | "Cannot allocate JSON structure in protobuf2json_process_field()" 176 | ); 177 | } 178 | 179 | return 0; 180 | } 181 | 182 | static int protobuf2json_process_message( 183 | const ProtobufCMessage *protobuf_message, 184 | json_t **json_message, 185 | char *error_string, 186 | size_t error_size 187 | ) { 188 | *json_message = json_object(); 189 | if (!*json_message) { 190 | SET_ERROR_STRING_AND_RETURN( 191 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 192 | "Cannot allocate JSON structure using json_object()" 193 | ); 194 | } 195 | 196 | json_t *json_value = NULL; 197 | 198 | unsigned i; 199 | for (i = 0; i < protobuf_message->descriptor->n_fields; i++) { 200 | const ProtobufCFieldDescriptor *field_descriptor = protobuf_message->descriptor->fields + i; 201 | const void *protobuf_value = ((const char *)protobuf_message) + field_descriptor->offset; 202 | const void *protobuf_value_quantifier = ((const char *)protobuf_message) + field_descriptor->quantifier_offset; 203 | 204 | if (field_descriptor->label == PROTOBUF_C_LABEL_REQUIRED) { 205 | json_value = NULL; 206 | 207 | int result = protobuf2json_process_field(field_descriptor, protobuf_value, &json_value, error_string, error_size); 208 | if (result) { 209 | return result; 210 | } 211 | 212 | if (json_object_set_new(*json_message, field_descriptor->name, json_value)) { 213 | SET_ERROR_STRING_AND_RETURN( 214 | PROTOBUF2JSON_ERR_JANSSON_INTERNAL, 215 | "Error in json_object_set_new()" 216 | ); 217 | } 218 | } else if (field_descriptor->label == PROTOBUF_C_LABEL_OPTIONAL) { 219 | if (field_descriptor->flags & PROTOBUF_C_FIELD_FLAG_ONEOF) { 220 | if (*(uint32_t *)protobuf_value_quantifier == field_descriptor->id) { 221 | if (field_descriptor->type == PROTOBUF_C_TYPE_MESSAGE || field_descriptor->type == PROTOBUF_C_TYPE_STRING) { 222 | if (protobuf_value == NULL || protobuf_value == field_descriptor->default_value) { 223 | continue; 224 | } 225 | } 226 | } else { 227 | continue; 228 | } 229 | } 230 | 231 | protobuf_c_boolean is_set = 0; 232 | 233 | if (field_descriptor->type == PROTOBUF_C_TYPE_MESSAGE || field_descriptor->type == PROTOBUF_C_TYPE_STRING) { 234 | if (*(const void * const *)protobuf_value) { 235 | is_set = 1; 236 | } 237 | } else { 238 | if (*(const protobuf_c_boolean *)protobuf_value_quantifier) { 239 | is_set = 1; 240 | } 241 | } 242 | 243 | if (is_set || field_descriptor->default_value) { 244 | json_value = NULL; 245 | 246 | int result = protobuf2json_process_field(field_descriptor, protobuf_value, &json_value, error_string, error_size); 247 | if (result) { 248 | return result; 249 | } 250 | 251 | if (json_object_set_new(*json_message, field_descriptor->name, json_value)) { 252 | SET_ERROR_STRING_AND_RETURN( 253 | PROTOBUF2JSON_ERR_JANSSON_INTERNAL, 254 | "Error in json_object_set_new()" 255 | ); 256 | } 257 | } 258 | } else { // PROTOBUF_C_LABEL_REPEATED 259 | const size_t *protobuf_values_count = (const size_t *)protobuf_value_quantifier; 260 | 261 | if (*protobuf_values_count) { 262 | json_t *array = json_array(); 263 | if (!array) { 264 | SET_ERROR_STRING_AND_RETURN( 265 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 266 | "Cannot allocate JSON structure using json_array()" 267 | ); 268 | } 269 | 270 | size_t value_size = protobuf2json_value_size_by_type(field_descriptor->type); 271 | if (!value_size) { 272 | SET_ERROR_STRING_AND_RETURN( 273 | PROTOBUF2JSON_ERR_UNSUPPORTED_FIELD_TYPE, 274 | "Cannot calculate value size for %d using protobuf2json_value_size_by_type()", 275 | field_descriptor->type 276 | ); 277 | } 278 | 279 | unsigned j; 280 | for (j = 0; j < *protobuf_values_count; j++) { 281 | const char *protobuf_value_repeated = (*(char * const *)protobuf_value) + j * value_size; 282 | 283 | json_value = NULL; 284 | 285 | int result = protobuf2json_process_field(field_descriptor, (const void *)protobuf_value_repeated, &json_value, error_string, error_size); 286 | if (result) { 287 | return result; 288 | } 289 | 290 | if (json_array_append_new(array, json_value)) { 291 | SET_ERROR_STRING_AND_RETURN( 292 | PROTOBUF2JSON_ERR_JANSSON_INTERNAL, 293 | "Error in json_array_append_new()" 294 | ); 295 | } 296 | } 297 | 298 | if (json_object_set_new(*json_message, field_descriptor->name, array)) { 299 | SET_ERROR_STRING_AND_RETURN( 300 | PROTOBUF2JSON_ERR_JANSSON_INTERNAL, 301 | "Error in json_object_set_new()" 302 | ); 303 | } 304 | } 305 | } 306 | } 307 | 308 | return 0; 309 | } 310 | 311 | /* === Protobuf -> JSON === Public === */ 312 | 313 | int protobuf2json_object( 314 | ProtobufCMessage *protobuf_message, 315 | json_t **json_object, 316 | char *error_string, 317 | size_t error_size 318 | ) { 319 | int ret = protobuf2json_process_message(protobuf_message, json_object, error_string, error_size); 320 | if (ret) { 321 | json_decref(*json_object); 322 | return ret; 323 | } 324 | 325 | return 0; 326 | } 327 | 328 | int protobuf2json_string( 329 | ProtobufCMessage *protobuf_message, 330 | size_t json_flags, 331 | char **json_string, 332 | char *error_string, 333 | size_t error_size 334 | ) { 335 | json_t *json_object = NULL; 336 | 337 | int ret = protobuf2json_object(protobuf_message, &json_object, error_string, error_size); 338 | if (ret) { 339 | return ret; 340 | } 341 | 342 | // NOTICE: Should be freed by caller 343 | *json_string = json_dumps(json_object, json_flags); 344 | if (!*json_string) { 345 | json_decref(json_object); 346 | 347 | SET_ERROR_STRING_AND_RETURN( 348 | PROTOBUF2JSON_ERR_CANNOT_DUMP_STRING, 349 | "Cannot dump JSON object to string using json_dumps()" 350 | ); 351 | } 352 | 353 | json_decref(json_object); 354 | return 0; 355 | } 356 | 357 | int protobuf2json_file( 358 | ProtobufCMessage *protobuf_message, 359 | size_t json_flags, 360 | char *json_file, 361 | char *fopen_mode, 362 | char *error_string, 363 | size_t error_size 364 | ) { 365 | char *json_string = NULL; 366 | FILE *fd = NULL; 367 | 368 | if (!json_file) { 369 | SET_ERROR_STRING_AND_RETURN( 370 | PROTOBUF2JSON_ERR_CANNOT_DUMP_FILE, 371 | "Cannot open NULL to dump JSON" 372 | ); 373 | } 374 | 375 | if (!fopen_mode) { 376 | SET_ERROR_STRING_AND_RETURN( 377 | PROTOBUF2JSON_ERR_CANNOT_DUMP_FILE, 378 | "Cannot open file with NULL fopen(3) mode to dump JSON" 379 | ); 380 | } 381 | 382 | int ret = protobuf2json_string(protobuf_message, json_flags, &json_string, error_string, error_size); 383 | if (ret) { 384 | return ret; 385 | } 386 | 387 | fd = fopen(json_file, fopen_mode); 388 | if (!fd) { 389 | free(json_string); 390 | 391 | SET_ERROR_STRING_AND_RETURN( 392 | PROTOBUF2JSON_ERR_CANNOT_DUMP_FILE, 393 | "Cannot open file '%s' with mode '%s' to dump JSON, errno=%d", 394 | json_file, fopen_mode, errno 395 | ); 396 | } 397 | 398 | if (fwrite(json_string, strlen(json_string), 1, fd) != 1) { 399 | fclose(fd); 400 | free(json_string); 401 | 402 | SET_ERROR_STRING_AND_RETURN( 403 | PROTOBUF2JSON_ERR_CANNOT_DUMP_FILE, 404 | "Cannot write JSON to file '%s' with mode '%s', errno=%d", 405 | json_file, fopen_mode, errno 406 | ); 407 | } 408 | 409 | fclose(fd); 410 | free(json_string); 411 | return 0; 412 | } 413 | 414 | /* === JSON -> Protobuf === Private === */ 415 | 416 | static int json2protobuf_process_message( 417 | json_t *json_object, 418 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 419 | ProtobufCMessage **protobuf_message, 420 | char *error_string, 421 | size_t error_size 422 | ); 423 | 424 | static const char* json2protobuf_integer_name_by_c_type(ProtobufCType type) { 425 | switch (type) { 426 | case PROTOBUF_C_TYPE_INT32: 427 | return "int32"; 428 | case PROTOBUF_C_TYPE_SINT32: 429 | return "sint32"; 430 | case PROTOBUF_C_TYPE_SFIXED32: 431 | return "sfixed32"; 432 | case PROTOBUF_C_TYPE_UINT32: 433 | return "uint32"; 434 | case PROTOBUF_C_TYPE_FIXED32: 435 | return "fixed32"; 436 | case PROTOBUF_C_TYPE_INT64: 437 | return "int64"; 438 | case PROTOBUF_C_TYPE_SINT64: 439 | return "sint64"; 440 | case PROTOBUF_C_TYPE_SFIXED64: 441 | return "sfixed64"; 442 | case PROTOBUF_C_TYPE_UINT64: 443 | return "uint64"; 444 | case PROTOBUF_C_TYPE_FIXED64: 445 | return "fixed64"; 446 | default: 447 | assert(0); 448 | return "unknown"; 449 | } 450 | } 451 | 452 | static int json2protobuf_process_field( 453 | const ProtobufCFieldDescriptor *field_descriptor, 454 | json_t *json_value, 455 | void *protobuf_value, 456 | char *error_string, 457 | size_t error_size 458 | ) { 459 | if (field_descriptor->type == PROTOBUF_C_TYPE_INT32 460 | || field_descriptor->type == PROTOBUF_C_TYPE_SINT32 461 | || field_descriptor->type == PROTOBUF_C_TYPE_SFIXED32 462 | ) { 463 | if (!json_is_integer(json_value)) { 464 | SET_ERROR_STRING_AND_RETURN( 465 | PROTOBUF2JSON_ERR_IS_NOT_INTEGER, 466 | "JSON value is not an integer required for GPB %s", 467 | json2protobuf_integer_name_by_c_type(field_descriptor->type) 468 | ); 469 | } 470 | 471 | int32_t value_int32_t = (int32_t)json_integer_value(json_value); 472 | 473 | memcpy(protobuf_value, &value_int32_t, sizeof(value_int32_t)); 474 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_UINT32 475 | || field_descriptor->type == PROTOBUF_C_TYPE_FIXED32 476 | ) { 477 | if (!json_is_integer(json_value)) { 478 | SET_ERROR_STRING_AND_RETURN( 479 | PROTOBUF2JSON_ERR_IS_NOT_INTEGER, 480 | "JSON value is not an integer required for GPB %s", 481 | json2protobuf_integer_name_by_c_type(field_descriptor->type) 482 | ); 483 | } 484 | 485 | uint32_t value_uint32_t = (uint32_t)json_integer_value(json_value); 486 | 487 | memcpy(protobuf_value, &value_uint32_t, sizeof(value_uint32_t)); 488 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_INT64 489 | || field_descriptor->type == PROTOBUF_C_TYPE_SINT64 490 | || field_descriptor->type == PROTOBUF_C_TYPE_SFIXED64 491 | ) { 492 | if (!json_is_integer(json_value)) { 493 | SET_ERROR_STRING_AND_RETURN( 494 | PROTOBUF2JSON_ERR_IS_NOT_INTEGER, 495 | "JSON value is not an integer required for GPB %s", 496 | json2protobuf_integer_name_by_c_type(field_descriptor->type) 497 | ); 498 | } 499 | 500 | int64_t value_int64_t = (int64_t)json_integer_value(json_value); 501 | 502 | memcpy(protobuf_value, &value_int64_t, sizeof(value_int64_t)); 503 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_UINT64 504 | || field_descriptor->type == PROTOBUF_C_TYPE_FIXED64 505 | ) { 506 | if (!json_is_integer(json_value)) { 507 | SET_ERROR_STRING_AND_RETURN( 508 | PROTOBUF2JSON_ERR_IS_NOT_INTEGER, 509 | "JSON value is not an integer required for GPB %s", 510 | json2protobuf_integer_name_by_c_type(field_descriptor->type) 511 | ); 512 | } 513 | 514 | uint64_t value_uint64_t = (uint64_t)json_integer_value(json_value); 515 | 516 | memcpy(protobuf_value, &value_uint64_t, sizeof(value_uint64_t)); 517 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_FLOAT) { 518 | float value_float; 519 | 520 | if (json_is_integer(json_value)) { 521 | value_float = (float)json_integer_value(json_value); 522 | } else if (json_is_real(json_value)) { 523 | value_float = (float)json_real_value(json_value); 524 | } else { 525 | SET_ERROR_STRING_AND_RETURN( 526 | PROTOBUF2JSON_ERR_IS_NOT_INTEGER_OR_REAL, 527 | "JSON value is not a integer/real required for GPB float" 528 | ); 529 | } 530 | 531 | memcpy(protobuf_value, &value_float, sizeof(value_float)); 532 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_DOUBLE) { 533 | double value_double; 534 | 535 | if (json_is_integer(json_value)) { 536 | value_double = (double)json_integer_value(json_value); 537 | } else if (json_is_real(json_value)) { 538 | value_double = (double)json_real_value(json_value); 539 | } else { 540 | SET_ERROR_STRING_AND_RETURN( 541 | PROTOBUF2JSON_ERR_IS_NOT_INTEGER_OR_REAL, 542 | "JSON value is not a integer/real required for GPB double" 543 | ); 544 | } 545 | 546 | memcpy(protobuf_value, &value_double, sizeof(value_double)); 547 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_BOOL) { 548 | if (!json_is_boolean(json_value)) { 549 | SET_ERROR_STRING_AND_RETURN( 550 | PROTOBUF2JSON_ERR_IS_NOT_BOOLEAN, 551 | "JSON value is not a boolean required for GPB bool" 552 | ); 553 | } 554 | 555 | protobuf_c_boolean value_boolean = (protobuf_c_boolean)json_boolean_value(json_value); 556 | 557 | memcpy(protobuf_value, &value_boolean, sizeof(value_boolean)); 558 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_ENUM) { 559 | if (!json_is_string(json_value)) { 560 | SET_ERROR_STRING_AND_RETURN( 561 | PROTOBUF2JSON_ERR_IS_NOT_STRING, 562 | "JSON value is not a string required for GPB enum" 563 | ); 564 | } 565 | 566 | const char* enum_value_name = json_string_value(json_value); 567 | 568 | const ProtobufCEnumValue *enum_value; 569 | 570 | enum_value = protobuf_c_enum_descriptor_get_value_by_name(field_descriptor->descriptor, enum_value_name); 571 | if (!enum_value) { 572 | SET_ERROR_STRING_AND_RETURN( 573 | PROTOBUF2JSON_ERR_UNKNOWN_ENUM_VALUE, 574 | "Unknown value '%s' for enum '%s'", 575 | enum_value_name, ((ProtobufCEnumDescriptor *)field_descriptor->descriptor)->name 576 | ); 577 | } 578 | 579 | int32_t value_enum = (int32_t)enum_value->value; 580 | 581 | memcpy(protobuf_value, &value_enum, sizeof(value_enum)); 582 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_STRING) { 583 | if (!json_is_string(json_value)) { 584 | SET_ERROR_STRING_AND_RETURN( 585 | PROTOBUF2JSON_ERR_IS_NOT_STRING, 586 | "JSON value is not a string required for GPB string" 587 | ); 588 | } 589 | 590 | const char* value_string = json_string_value(json_value); 591 | size_t value_string_length = strlen(value_string); 592 | 593 | char* value_string_copy = calloc(value_string_length + 1, sizeof(char)); 594 | if (!value_string_copy) { 595 | SET_ERROR_STRING_AND_RETURN( 596 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 597 | "Cannot allocate %zu bytes using calloc(3)", 598 | (value_string_length + 1) * sizeof(char) 599 | ); 600 | } 601 | 602 | memcpy(value_string_copy, value_string, value_string_length + 1); 603 | 604 | *(char **)(protobuf_value) = value_string_copy; 605 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_BYTES) { 606 | if (!json_is_string(json_value)) { 607 | SET_ERROR_STRING_AND_RETURN( 608 | PROTOBUF2JSON_ERR_IS_NOT_STRING, 609 | "JSON value is not a string required for GPB bytes" 610 | ); 611 | } 612 | 613 | const char* value_string = json_string_value(json_value); 614 | size_t value_string_length = json_string_length(json_value); 615 | 616 | int base64_decoded_length = base64_decoded_len(value_string_length); 617 | 618 | char* base64_decoded_data = calloc(base64_decoded_length, sizeof(char)); 619 | if (!base64_decoded_data) { 620 | SET_ERROR_STRING_AND_RETURN( 621 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 622 | "Cannot allocate %zu bytes using calloc(3)", 623 | base64_decoded_length * sizeof(char) 624 | ); 625 | } 626 | 627 | /* @todo: check for zero length / error */ 628 | base64_decoded_length = base64_decode(base64_decoded_data, value_string, value_string_length); 629 | 630 | char* value_string_copy = calloc(base64_decoded_length, sizeof(char)); 631 | if (!value_string_copy) { 632 | SET_ERROR_STRING_AND_RETURN( 633 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 634 | "Cannot allocate %zu bytes using calloc(3)", 635 | base64_decoded_length * sizeof(char) 636 | ); 637 | } 638 | 639 | memcpy(value_string_copy, base64_decoded_data, base64_decoded_length); 640 | 641 | free(base64_decoded_data); 642 | 643 | ProtobufCBinaryData value_binary; 644 | 645 | value_binary.data = (uint8_t *)value_string_copy; 646 | value_binary.len = base64_decoded_length; 647 | 648 | memcpy(protobuf_value, &value_binary, sizeof(value_binary)); 649 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_MESSAGE) { 650 | ProtobufCMessage *protobuf_message; 651 | 652 | int result = json2protobuf_process_message(json_value, field_descriptor->descriptor, &protobuf_message, error_string, error_size); 653 | if (result) { 654 | return result; 655 | } 656 | 657 | memcpy(protobuf_value, &protobuf_message, sizeof(protobuf_message)); 658 | } else { 659 | assert(0); 660 | } 661 | 662 | return 0; 663 | } 664 | 665 | #define SAFE_FREE_BITMAP_AND_MESSAGE \ 666 | do { \ 667 | if (presented_fields) { \ 668 | bitmap_free(presented_fields); \ 669 | } \ 670 | if (protobuf_message) { \ 671 | protobuf_c_message_free_unpacked(*protobuf_message, NULL); \ 672 | *protobuf_message = NULL; \ 673 | } \ 674 | } while (0) 675 | 676 | static int json2protobuf_process_message( 677 | json_t *json_object, 678 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 679 | ProtobufCMessage **protobuf_message, 680 | char *error_string, 681 | size_t error_size 682 | ) { 683 | bitmap_t presented_fields = NULL; 684 | 685 | int result = 0; 686 | 687 | if (!json_is_object(json_object)) { 688 | SET_ERROR_STRING_AND_RETURN( 689 | PROTOBUF2JSON_ERR_IS_NOT_OBJECT, 690 | "JSON is not an object required for GPB message" 691 | ); 692 | } 693 | 694 | *protobuf_message = calloc(1, protobuf_message_descriptor->sizeof_message); 695 | if (!*protobuf_message) { 696 | SET_ERROR_STRING_AND_RETURN( 697 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 698 | "Cannot allocate %zu bytes using calloc(3)", 699 | protobuf_message_descriptor->sizeof_message 700 | ); 701 | } 702 | 703 | protobuf_c_message_init(protobuf_message_descriptor, *protobuf_message); 704 | 705 | presented_fields = bitmap_alloc(protobuf_message_descriptor->n_fields); 706 | if (!presented_fields) { 707 | SAFE_FREE_BITMAP_AND_MESSAGE; 708 | 709 | SET_ERROR_STRING_AND_RETURN( 710 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 711 | "Cannot allocate bitmap structure using bitmap_alloc()" 712 | ); 713 | } 714 | 715 | const char *json_key; 716 | json_t *json_object_value; 717 | json_object_foreach(json_object, json_key, json_object_value) { 718 | const ProtobufCFieldDescriptor *field_descriptor = protobuf_c_message_descriptor_get_field_by_name(protobuf_message_descriptor, json_key); 719 | if (!field_descriptor) { 720 | SAFE_FREE_BITMAP_AND_MESSAGE; 721 | 722 | SET_ERROR_STRING_AND_RETURN( 723 | PROTOBUF2JSON_ERR_UNKNOWN_FIELD, 724 | "Unknown field '%s' for message '%s'", 725 | json_key, protobuf_message_descriptor->name 726 | ); 727 | } 728 | 729 | unsigned int field_number = field_descriptor - protobuf_message_descriptor->fields; 730 | 731 | // This cannot happen because Jansson handle this on his side 732 | /*if (bitmap_get(presented_fields, field_number)) { 733 | SAFE_FREE_BITMAP_AND_MESSAGE; 734 | 735 | SET_ERROR_STRING_AND_RETURN( 736 | PROTOBUF2JSON_ERR_DUPLICATE_FIELD, 737 | "Duplicate field '%s' for message '%s'", 738 | json_key, protobuf_message_descriptor->name 739 | ); 740 | }*/ 741 | bitmap_set(presented_fields, field_number); 742 | 743 | void *protobuf_value = ((char *)*protobuf_message) + field_descriptor->offset; 744 | void *protobuf_value_quantifier = ((char *)*protobuf_message) + field_descriptor->quantifier_offset; 745 | 746 | if (field_descriptor->flags & PROTOBUF_C_FIELD_FLAG_ONEOF) { 747 | *(uint32_t*)protobuf_value_quantifier = field_descriptor->id; 748 | } 749 | 750 | if (field_descriptor->label == PROTOBUF_C_LABEL_REQUIRED) { 751 | result = json2protobuf_process_field(field_descriptor, json_object_value, protobuf_value, error_string, error_size); 752 | if (result) { 753 | SAFE_FREE_BITMAP_AND_MESSAGE; 754 | 755 | return result; 756 | } 757 | } else if (field_descriptor->label == PROTOBUF_C_LABEL_OPTIONAL) { 758 | if (field_descriptor->type == PROTOBUF_C_TYPE_MESSAGE || field_descriptor->type == PROTOBUF_C_TYPE_STRING 759 | || (field_descriptor->flags & PROTOBUF_C_FIELD_FLAG_ONEOF)) { 760 | // Do nothing 761 | } else { 762 | *(protobuf_c_boolean *)protobuf_value_quantifier = 1; 763 | } 764 | 765 | result = json2protobuf_process_field(field_descriptor, json_object_value, protobuf_value, error_string, error_size); 766 | if (result) { 767 | SAFE_FREE_BITMAP_AND_MESSAGE; 768 | 769 | return result; 770 | } 771 | } else { // PROTOBUF_C_LABEL_REPEATED 772 | if (!json_is_array(json_object_value)) { 773 | SAFE_FREE_BITMAP_AND_MESSAGE; 774 | 775 | SET_ERROR_STRING_AND_RETURN( 776 | PROTOBUF2JSON_ERR_IS_NOT_ARRAY, 777 | "JSON is not an array required for repeatable GPB field" 778 | ); 779 | } 780 | 781 | size_t *protobuf_values_count = (size_t *)protobuf_value_quantifier; 782 | 783 | *protobuf_values_count = json_array_size(json_object_value); 784 | 785 | if (*protobuf_values_count) { 786 | size_t value_size = protobuf2json_value_size_by_type(field_descriptor->type); 787 | if (!value_size) { 788 | SAFE_FREE_BITMAP_AND_MESSAGE; 789 | 790 | SET_ERROR_STRING_AND_RETURN( 791 | PROTOBUF2JSON_ERR_UNSUPPORTED_FIELD_TYPE, 792 | "Cannot calculate value size for %d using protobuf2json_value_size_by_type()", 793 | field_descriptor->type 794 | ); 795 | } 796 | 797 | void *protobuf_value_repeated = calloc(*protobuf_values_count, value_size); 798 | if (!protobuf_value_repeated) { 799 | SAFE_FREE_BITMAP_AND_MESSAGE; 800 | 801 | SET_ERROR_STRING_AND_RETURN( 802 | PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY, 803 | "Cannot allocate %zu bytes using calloc(3)", 804 | (size_t)*protobuf_values_count * value_size 805 | ); 806 | } 807 | 808 | size_t json_index; 809 | json_t *json_array_value; 810 | json_array_foreach(json_object_value, json_index, json_array_value) { 811 | char *protobuf_value_repeated_value = (char *)protobuf_value_repeated + json_index * value_size; 812 | 813 | result = json2protobuf_process_field(field_descriptor, json_array_value, (void *)protobuf_value_repeated_value, error_string, error_size); 814 | if (result) { 815 | /* Free already processed repeated field items */ 816 | { 817 | if (field_descriptor->type == PROTOBUF_C_TYPE_STRING) { 818 | size_t t; 819 | for (t = 0; t <= json_index; t++) { 820 | free(((char **)protobuf_value_repeated)[t]); 821 | } 822 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_BYTES) { 823 | size_t t; 824 | for (t = 0; t <= json_index; t++) { 825 | free(((ProtobufCBinaryData *)protobuf_value_repeated)[t].data); 826 | } 827 | } else if (field_descriptor->type == PROTOBUF_C_TYPE_MESSAGE) { 828 | size_t t; 829 | for (t = 0; t <= json_index; t++) { 830 | if (((ProtobufCMessage **)protobuf_value_repeated)[t]) { 831 | protobuf_c_message_free_unpacked( 832 | ((ProtobufCMessage **)protobuf_value_repeated)[t], 833 | NULL 834 | ); 835 | } 836 | } 837 | } 838 | 839 | free(protobuf_value_repeated); 840 | *protobuf_values_count = 0; 841 | } 842 | 843 | SAFE_FREE_BITMAP_AND_MESSAGE; 844 | 845 | return result; 846 | } 847 | } 848 | 849 | memcpy(protobuf_value, &protobuf_value_repeated, sizeof(protobuf_value_repeated)); 850 | } 851 | } 852 | } 853 | 854 | unsigned int i = 0; 855 | for (i = 0; i < protobuf_message_descriptor->n_fields; i++) { 856 | const ProtobufCFieldDescriptor *field_descriptor = protobuf_message_descriptor->fields + i; 857 | 858 | if ((field_descriptor->label == PROTOBUF_C_LABEL_REQUIRED) && !field_descriptor->default_value && !bitmap_get(presented_fields, i)) { 859 | SAFE_FREE_BITMAP_AND_MESSAGE; 860 | 861 | SET_ERROR_STRING_AND_RETURN( 862 | PROTOBUF2JSON_ERR_REQUIRED_IS_MISSING, 863 | "Required field '%s' is missing in message '%s'", 864 | field_descriptor->name, protobuf_message_descriptor->name 865 | ); 866 | } 867 | } 868 | 869 | bitmap_free(presented_fields); 870 | 871 | return 0; 872 | } 873 | 874 | /* === JSON -> Protobuf === Public === */ 875 | 876 | int json2protobuf_object( 877 | json_t *json_object, 878 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 879 | ProtobufCMessage **protobuf_message, 880 | char *error_string, 881 | size_t error_size 882 | ) { 883 | int result = json2protobuf_process_message(json_object, protobuf_message_descriptor, protobuf_message, error_string, error_size); 884 | if (result) { 885 | return result; 886 | } 887 | 888 | return 0; 889 | } 890 | 891 | int json2protobuf_string( 892 | char *json_string, 893 | size_t json_flags, 894 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 895 | ProtobufCMessage **protobuf_message, 896 | char *error_string, 897 | size_t error_size 898 | ) { 899 | json_t *json_object = NULL; 900 | json_error_t error; 901 | 902 | json_object = json_loads(json_string, json_flags, &error); 903 | if (!json_object) { 904 | json_decref(json_object); 905 | 906 | SET_ERROR_STRING_AND_RETURN( 907 | PROTOBUF2JSON_ERR_CANNOT_PARSE_STRING, 908 | "JSON parsing error at line %d column %d (position %d): %s", 909 | error.line, error.column, error.position, error.text 910 | ); 911 | } 912 | 913 | int result = json2protobuf_object(json_object, protobuf_message_descriptor, protobuf_message, error_string, error_size); 914 | if (result) { 915 | json_decref(json_object); 916 | return result; 917 | } 918 | 919 | json_decref(json_object); 920 | return 0; 921 | } 922 | 923 | int json2protobuf_file( 924 | char *json_file, 925 | size_t json_flags, 926 | const ProtobufCMessageDescriptor *protobuf_message_descriptor, 927 | ProtobufCMessage **protobuf_message, 928 | char *error_string, 929 | size_t error_size 930 | ) { 931 | json_t *json_object = NULL; 932 | json_error_t error; 933 | 934 | json_object = json_load_file(json_file, json_flags, &error); 935 | if (!json_object) { 936 | json_decref(json_object); 937 | 938 | SET_ERROR_STRING_AND_RETURN( 939 | PROTOBUF2JSON_ERR_CANNOT_PARSE_FILE, 940 | "JSON parsing error at line %d column %d (position %d): %s", 941 | error.line, error.column, error.position, error.text 942 | ); 943 | } 944 | 945 | int result = json2protobuf_object(json_object, protobuf_message_descriptor, protobuf_message, error_string, error_size); 946 | if (result) { 947 | json_decref(json_object); 948 | return result; 949 | } 950 | 951 | return 0; 952 | } 953 | 954 | /* === END === */ 955 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | .deps 4 | .libs 5 | *.pb-c.[ch] 6 | run-tests 7 | run-tmp 8 | run-benchmarks 9 | -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014-2016 Oleg Efimov 2 | # 3 | # protobuf2json-c is free software; you can redistribute it 4 | # and/or modify it under the terms of the MIT license. 5 | # See LICENSE for details. 6 | 7 | # protoc 8 | 9 | BUILT_SOURCES = 10 | BUILT_SOURCES += test.pb-c.c 11 | 12 | MOSTLYCLEANFILES = 13 | MOSTLYCLEANFILES += *.pb-c.[ch] 14 | 15 | %.pb-c.c %.pb-c.h: %.proto 16 | $(PROTOBUF_C_COMPILER) --c_out=$(abs_builddir)/ -I`dirname $<` $< 17 | 18 | # check 19 | 20 | check_PROGRAMS = run-tests run-benchmarks run-tmp 21 | 22 | AM_CFLAGS = -I$(top_srcdir)/include 23 | AM_CFLAGS += $(PROTOBUF_C_INCLUDES) 24 | AM_CFLAGS += $(PROTOBUF_C_CFLAGS) 25 | AM_CFLAGS += $(LIBJANSSON_INCLUDES) 26 | AM_CFLAGS += $(MY_SANITIZE_CFLAGS) 27 | AM_CFLAGS += $(MY_VALGRIND_CFLAGS) 28 | AM_CFLAGS += $(MY_COVERAGE_CFLAGS) 29 | 30 | AM_LDFLAGS = -static 31 | AM_LDFLAGS += $(MY_VALGRIND_LDFLAGS) 32 | AM_LDFLAGS += $(MY_COVERAGE_LDFLAGS) 33 | 34 | LDADD = 35 | LDADD += $(PROTOBUF_C_LIBS) 36 | LDADD += $(LIBJANSSON_LIBS) 37 | LDADD += $(MY_SANITIZE_LIBS) 38 | LDADD += $(top_srcdir)/src/libprotobuf2json-c.la 39 | 40 | # run-tests 41 | 42 | run_tests_SOURCES = run-tests.c \ 43 | test-list.h \ 44 | test-protobuf2json-file.c \ 45 | test-protobuf2json-string.c \ 46 | test-json2protobuf-file.c \ 47 | test-json2protobuf-string.c \ 48 | test-reversible.c \ 49 | runner.c \ 50 | runner.h \ 51 | task.h \ 52 | test.pb-c.c 53 | 54 | if WINNT 55 | run_tests_SOURCES += runner-win.c \ 56 | runner-win.h 57 | else 58 | run_tests_SOURCES += runner-unix.c \ 59 | runner-unix.h 60 | endif 61 | 62 | # run-benchmarks 63 | 64 | run_benchmarks_SOURCES = run-benchmarks.c \ 65 | benchmarks-list.h \ 66 | benchmark-dummy.c \ 67 | runner.c \ 68 | runner.h \ 69 | task.h \ 70 | test.pb-c.c 71 | 72 | if WINNT 73 | run_benchmarks_SOURCES += runner-win.c \ 74 | runner-win.h 75 | else 76 | run_benchmarks_SOURCES += runner-unix.c \ 77 | runner-unix.h 78 | endif 79 | 80 | # run-tmp 81 | 82 | run_tmp_SOURCES = run-tmp.c \ 83 | test.pb-c.c 84 | -------------------------------------------------------------------------------- /test/benchmark-dummy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #include "task.h" 10 | #include "getrusage-helper.h" 11 | 12 | 13 | BENCHMARK_IMPL(dummy) { 14 | double ru_stime = 0, ru_utime = 0; 15 | int i = 0, r = 0; 16 | 17 | if (getrusage_helper(&ru_stime, &ru_utime)) { 18 | FATAL("getrusage_helper failed"); 19 | } 20 | 21 | for (i = 0; i < 1000000; i++) { 22 | ASSERT(r == 0); 23 | } 24 | 25 | if (getrusage_helper_sub(&ru_stime, &ru_utime, ru_stime, ru_utime)) { 26 | FATAL("getrusage_helper_sub failed"); 27 | } 28 | 29 | getrusage_helper_printf("Dummy", ru_stime, ru_utime); 30 | 31 | RETURN_OK(); 32 | } 33 | -------------------------------------------------------------------------------- /test/benchmark-list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | BENCHMARK_DECLARE (dummy) 10 | 11 | TASK_LIST_START 12 | BENCHMARK_ENTRY (dummy) 13 | TASK_LIST_END 14 | -------------------------------------------------------------------------------- /test/failed-alloc-helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #ifndef FAILED_ALLOC_HELPER_H_ 10 | #define FAILED_ALLOC_HELPER_H_ 11 | 12 | size_t failed_malloc_size = 0; 13 | int failed_malloc_count = 0; 14 | 15 | static void* failed_malloc(size_t size) { 16 | if (failed_malloc_count < 0) { 17 | if (size == failed_malloc_size) { 18 | return NULL; 19 | } 20 | } else if (failed_malloc_count == 0) { 21 | return NULL; 22 | } else { 23 | failed_malloc_count--; 24 | } 25 | 26 | return malloc(size); 27 | } 28 | 29 | static void failed_alloc_json_set_by_size(size_t new_failed_malloc_size) { 30 | failed_malloc_size = new_failed_malloc_size; 31 | failed_malloc_count = -1; 32 | 33 | json_set_alloc_funcs(failed_malloc, free); 34 | } 35 | 36 | static void failed_alloc_json_set_by_count(int new_failed_malloc_count) { 37 | failed_malloc_count = new_failed_malloc_count; 38 | 39 | json_set_alloc_funcs(failed_malloc, free); 40 | } 41 | 42 | static void failed_alloc_json_unset() { 43 | json_set_alloc_funcs(malloc, free); 44 | } 45 | 46 | #endif /* FAILED_ALLOC_HELPER_H_ */ 47 | -------------------------------------------------------------------------------- /test/fixtures/bad_json.json: -------------------------------------------------------------------------------- 1 | ... 2 | -------------------------------------------------------------------------------- /test/fixtures/bad_message.json: -------------------------------------------------------------------------------- 1 | { 2 | "unknown_field": "unknown_field_value" 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/good.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "John Doe", 3 | "id": 42 4 | } 5 | -------------------------------------------------------------------------------- /test/getrusage-helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #ifndef GETRUSAGE_HELPER_H_ 10 | #define GETRUSAGE_HELPER_H_ 11 | 12 | #include 13 | #include 14 | 15 | static int getrusage_helper(double *ru_stime, double *ru_utime) { 16 | struct rusage usage; 17 | 18 | if (getrusage(RUSAGE_SELF, &usage)) 19 | return -errno; 20 | 21 | *ru_stime = (double)usage.ru_stime.tv_sec + (double)usage.ru_stime.tv_usec / 1000000.0; 22 | *ru_utime = (double)usage.ru_utime.tv_sec + (double)usage.ru_utime.tv_usec / 1000000.0; 23 | 24 | return 0; 25 | } 26 | 27 | static int getrusage_helper_sub(double *ru_stime, double *ru_utime, double ru_stime_sub, double ru_utime_sub) { 28 | struct rusage usage; 29 | 30 | if (getrusage(RUSAGE_SELF, &usage)) 31 | return -errno; 32 | 33 | *ru_stime = (double)usage.ru_stime.tv_sec + (double)usage.ru_stime.tv_usec / 1000000.0 - ru_stime_sub; 34 | *ru_utime = (double)usage.ru_utime.tv_sec + (double)usage.ru_utime.tv_usec / 1000000.0 - ru_utime_sub; 35 | 36 | return 0; 37 | } 38 | 39 | static void getrusage_helper_printf(const char *message, double ru_stime, double ru_utime) { 40 | printf("%s rusage: %.5f system, %.5f user\n", message, ru_stime, ru_utime); 41 | } 42 | 43 | #endif /* GETRUSAGE_HELPER_H_ */ 44 | -------------------------------------------------------------------------------- /test/run-benchmarks.c: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #include "runner.h" 26 | #include "task.h" 27 | 28 | /* Actual benchmarks and helpers are defined in benchmark-list.h */ 29 | #include "benchmark-list.h" 30 | 31 | 32 | static int maybe_run_test(int argc, char **argv) { 33 | if ((strcmp(argv[1], "--list") == 0) || (strcmp(argv[1], "-l") == 0)) { 34 | print_tests(); 35 | return 0; 36 | } 37 | 38 | if ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0)) { 39 | print_help(argv[0]); 40 | return 0; 41 | } 42 | 43 | return run_test(argv[1], 1, 1); 44 | } 45 | 46 | 47 | int main(int argc, char **argv) { 48 | platform_init(argc, argv); 49 | 50 | switch (argc) { 51 | case 1: return run_tests(1); 52 | case 2: return maybe_run_test(argc, argv); 53 | case 3: return run_test_part(argv[1], argv[2]); 54 | default: 55 | LOGF("Too many arguments.\n"); 56 | return 1; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/run-tests.c: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #ifdef _WIN32 27 | # include 28 | #else 29 | # include 30 | #endif 31 | 32 | #include "runner.h" 33 | #include "task.h" 34 | 35 | /* Actual tests and helpers are defined in test-list.h */ 36 | #include "test-list.h" 37 | 38 | 39 | static int maybe_run_test(int argc, char **argv) { 40 | if ((strcmp(argv[1], "--list") == 0) || (strcmp(argv[1], "-l") == 0)) { 41 | print_tests(); 42 | return 0; 43 | } 44 | 45 | if ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0)) { 46 | print_help(argv[0]); 47 | return 0; 48 | } 49 | 50 | return run_test(argv[1], 0, 1); 51 | } 52 | 53 | 54 | int main(int argc, char **argv) { 55 | platform_init(argc, argv); 56 | 57 | switch (argc) { 58 | case 1: return run_tests(0); 59 | case 2: return maybe_run_test(argc, argv); 60 | case 3: return run_test_part(argv[1], argv[2]); 61 | default: 62 | LOGF("Too many arguments.\n"); 63 | return 1; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/run-tmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #include "task.h" 10 | #include "test.pb-c.h" 11 | #include "protobuf2json.h" 12 | 13 | #include /* dirname */ 14 | 15 | char executable_path[MAXPATHLEN] = {0}; 16 | 17 | void oneof(void) { 18 | int result; 19 | 20 | Foo__Something something = FOO__SOMETHING__INIT; 21 | 22 | char *json_string; 23 | 24 | // FOO__SOMETHING__SOMETHING__NOT_SET 25 | something.something_case = FOO__SOMETHING__SOMETHING__NOT_SET; 26 | result = protobuf2json_string(&something.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 27 | 28 | printf("Debug: %s\n", json_string); 29 | free(json_string); 30 | 31 | // FOO__SOMETHING__SOMETHING_ONEOF_STRING 32 | something.oneof_string = "string"; 33 | something.something_case = FOO__SOMETHING__SOMETHING_ONEOF_STRING; 34 | result = protobuf2json_string(&something.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 35 | 36 | printf("Debug: %s\n", json_string); 37 | free(json_string); 38 | 39 | // FOO__SOMETHING__SOMETHING_ONEOF_BYTES 40 | something.oneof_bytes.len = 5; 41 | something.oneof_bytes.data = (uint8_t*)"bytes"; 42 | something.something_case = FOO__SOMETHING__SOMETHING_ONEOF_BYTES; 43 | result = protobuf2json_string(&something.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 44 | 45 | printf("Debug: %s\n", json_string); 46 | free(json_string); 47 | } 48 | 49 | void person__debug(void) { 50 | int result; 51 | 52 | const char *initial_json_string = \ 53 | "{\n" 54 | " \"name\": \"John Doe\",\n" 55 | " \"id\": 42,\n" 56 | " \"phone\": [\n" 57 | " {\n" 58 | " \"number\": \"+123456789\",\n" 59 | " \"type\": \"WORK\"\n" 60 | " },\n" 61 | " {\n" 62 | " \"number\": \"+987654321\",\n" 63 | " \"type\": \"MOBILE\"\n" 64 | " },\n" 65 | " {\n" 66 | " \"number\": \"+555555555\"\n" 67 | " }\n" 68 | " ]\n" 69 | "}" 70 | ; 71 | 72 | ProtobufCMessage *protobuf_message = NULL; 73 | 74 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, NULL, 0); 75 | ASSERT_ZERO(result); 76 | 77 | Foo__Person *person = (Foo__Person *)protobuf_message; 78 | 79 | //asm volatile ("int $3"); 80 | 81 | printf("Debug: person->id: %d\n", person->id); 82 | printf("Debug: person->name: %s\n", person->name); 83 | printf("Debug: person->n_phone: %zu\n", person->n_phone); 84 | printf("Debug: person->phone[0]->number: %s\n", person->phone[0]->number); 85 | printf("Debug: person->phone[0]->type: %d\n", person->phone[0]->type); 86 | printf("Debug: person->phone[1]->number: %s\n", person->phone[1]->number); 87 | printf("Debug: person->phone[1]->type: %d\n", person->phone[1]->type); 88 | printf("Debug: person->phone[2]->number: %s\n", person->phone[2]->number); 89 | printf("Debug: person->phone[2]->type: %d\n", person->phone[2]->type); 90 | 91 | ASSERT(person->id == 42); 92 | ASSERT_STRCMP(person->name, "John Doe"); 93 | 94 | char *json_string; 95 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 96 | ASSERT_ZERO(result); 97 | ASSERT(json_string); 98 | 99 | const char *expected_json_string = \ 100 | "{\n" 101 | " \"name\": \"John Doe\",\n" 102 | " \"id\": 42,\n" 103 | " \"phone\": [\n" 104 | " {\n" 105 | " \"number\": \"+123456789\",\n" 106 | " \"type\": \"WORK\"\n" 107 | " },\n" 108 | " {\n" 109 | " \"number\": \"+987654321\",\n" 110 | " \"type\": \"MOBILE\"\n" 111 | " },\n" 112 | " {\n" 113 | " \"number\": \"+555555555\",\n" 114 | " \"type\": \"HOME\"\n" 115 | " }\n" 116 | " ]\n" 117 | "}" 118 | ; 119 | 120 | ASSERT_STRCMP( 121 | json_string, 122 | expected_json_string 123 | ); 124 | 125 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 126 | free(json_string); 127 | 128 | printf("Debug: %s OK\n", __FUNCTION__); 129 | } 130 | 131 | void person__error_unknown_enum_value(void) { 132 | int result; 133 | char error_string[256] = {0}; 134 | 135 | const char *initial_json_string = \ 136 | "{\n" 137 | " \"name\": \"John Doe\",\n" 138 | " \"id\": 42,\n" 139 | " \"phone\": [\n" 140 | " {\n" 141 | " \"number\": \"+123456789\",\n" 142 | " \"type\": \"UNKNOWN\"\n" 143 | " }\n" 144 | " ]\n" 145 | "}" 146 | ; 147 | 148 | ProtobufCMessage *protobuf_message = NULL; 149 | 150 | //asm volatile ("int $3"); 151 | 152 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 153 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_UNKNOWN_ENUM_VALUE); 154 | 155 | const char *expected_error_string = \ 156 | "Unknown value 'UNKNOWN' for enum 'Foo.Person.PhoneType'" 157 | ; 158 | 159 | printf("Error: %s\n", error_string); 160 | 161 | ASSERT_STRCMP( 162 | error_string, 163 | expected_error_string 164 | ); 165 | 166 | printf("Debug: %s OK\n", __FUNCTION__); 167 | } 168 | 169 | void person__bad_json_string(void) { 170 | int result; 171 | char error_string[256] = {0}; 172 | 173 | const char *initial_json_string = "..."; 174 | 175 | ProtobufCMessage *protobuf_message = NULL; 176 | 177 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 178 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_PARSE_STRING); 179 | 180 | const char *expected_error_string = \ 181 | "JSON parsing error at line 1 column 1 (position 1): " 182 | "'[' or '{' expected near '.'" 183 | ; 184 | 185 | printf("Error: %s\n", error_string); 186 | 187 | ASSERT_STRCMP( 188 | error_string, 189 | expected_error_string 190 | ); 191 | 192 | printf("Debug: %s OK\n", __FUNCTION__); 193 | } 194 | 195 | void person__error_is_not_array(void) { 196 | int result; 197 | char error_string[256] = {0}; 198 | 199 | const char *initial_json_string = \ 200 | "{\n" 201 | " \"phone\": {}\n" 202 | "}" 203 | ; 204 | 205 | ProtobufCMessage *protobuf_message = NULL; 206 | 207 | //asm volatile ("int $3"); 208 | 209 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 210 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_ARRAY); 211 | 212 | const char *expected_error_string = \ 213 | "JSON is not an array required for repeatable GPB field" 214 | ; 215 | 216 | printf("Error: %s\n", error_string); 217 | 218 | ASSERT_STRCMP( 219 | error_string, 220 | expected_error_string 221 | ); 222 | 223 | printf("Debug: %s OK\n", __FUNCTION__); 224 | } 225 | 226 | void person__error_unknown_field(void) { 227 | int result; 228 | char error_string[256] = {0}; 229 | 230 | const char *initial_json_string = \ 231 | "{\n" 232 | " \"unknown_field\": \"unknown value\"\n" 233 | "}" 234 | ; 235 | 236 | ProtobufCMessage *protobuf_message = NULL; 237 | 238 | //asm volatile ("int $3"); 239 | 240 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 241 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_UNKNOWN_FIELD); 242 | 243 | const char *expected_error_string = \ 244 | "Unknown field 'unknown_field' for message 'Foo.Person'" 245 | ; 246 | 247 | printf("Error: %s\n", error_string); 248 | 249 | ASSERT_STRCMP( 250 | error_string, 251 | expected_error_string 252 | ); 253 | 254 | printf("Debug: %s OK\n", __FUNCTION__); 255 | } 256 | 257 | void person__error_unknown_field_nested(void) { 258 | int result; 259 | char error_string[256] = {0}; 260 | 261 | const char *initial_json_string = \ 262 | "{\n" 263 | " \"name\": \"John Doe\",\n" 264 | " \"id\": 42,\n" 265 | " \"phone\": [\n" 266 | " {\n" 267 | " \"unknown_field\": \"unknown value\"\n" 268 | " }\n" 269 | " ]\n" 270 | "}" 271 | ; 272 | 273 | ProtobufCMessage *protobuf_message = NULL; 274 | 275 | //asm volatile ("int $3"); 276 | 277 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 278 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_UNKNOWN_FIELD); 279 | 280 | const char *expected_error_string = \ 281 | "Unknown field 'unknown_field' for message 'Foo.Person.PhoneNumber'" 282 | ; 283 | 284 | printf("Error: %s\n", error_string); 285 | 286 | ASSERT_STRCMP( 287 | error_string, 288 | expected_error_string 289 | ); 290 | 291 | printf("Debug: %s OK\n", __FUNCTION__); 292 | } 293 | 294 | void repeated_values__error_is_not_string_required_for_string(void) { 295 | int result; 296 | char error_string[256] = {0}; 297 | 298 | const char *initial_json_string = \ 299 | "{\n" 300 | " \"value_string\": [\"42\", 42]\n" 301 | "}" 302 | ; 303 | 304 | ProtobufCMessage *protobuf_message = NULL; 305 | 306 | //asm volatile ("int $3"); 307 | 308 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, error_string, sizeof(error_string)); 309 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_STRING); 310 | 311 | const char *expected_error_string = \ 312 | "JSON value is not a string required for GPB string" 313 | ; 314 | 315 | ASSERT_STRCMP( 316 | error_string, 317 | expected_error_string 318 | ); 319 | 320 | printf("Debug: %s OK\n", __FUNCTION__); 321 | } 322 | 323 | void read_file_success(void) { 324 | int result; 325 | 326 | char file_path[MAXPATHLEN] = {0}; 327 | char file_name[MAXPATHLEN] = {0}; 328 | 329 | result = realpath(dirname(executable_path), file_path) ? 1 : 0; 330 | ASSERT(result > 0); 331 | 332 | result = snprintf(file_name, sizeof(file_name) - 1, "%s/fixtures/good.json", file_path); 333 | ASSERT(result > 0); 334 | 335 | ProtobufCMessage *protobuf_message = NULL; 336 | 337 | result = json2protobuf_file((char *)file_name, 0, &foo__person__descriptor, &protobuf_message, NULL, 0); 338 | ASSERT_ZERO(result); 339 | 340 | char *json_string; 341 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 342 | ASSERT_ZERO(result); 343 | ASSERT(json_string); 344 | 345 | ASSERT_STRCMP( 346 | json_string, 347 | "{\n" 348 | " \"name\": \"John Doe\",\n" 349 | " \"id\": 42\n" 350 | "}" 351 | ); 352 | 353 | free(json_string); 354 | 355 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 356 | 357 | printf("Debug: %s OK\n", __FUNCTION__); 358 | } 359 | 360 | void bytes_encode(void) { 361 | int result; 362 | 363 | Foo__Something something = FOO__SOMETHING__INIT; 364 | 365 | char *json_string; 366 | 367 | // FOO__SOMETHING__SOMETHING_ONEOF_BYTES 368 | something.oneof_bytes.len = 1; 369 | something.oneof_bytes.data = (uint8_t*)"\0"; 370 | something.something_case = FOO__SOMETHING__SOMETHING_ONEOF_BYTES; 371 | result = protobuf2json_string(&something.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 372 | 373 | printf("Debug: %s\n", json_string); 374 | free(json_string); 375 | } 376 | 377 | int main(int argc, char **argv) { 378 | strncpy(executable_path, argv[0], sizeof(executable_path) - 1); 379 | 380 | oneof(); 381 | 382 | person__debug(); 383 | 384 | person__error_unknown_enum_value(); 385 | 386 | person__bad_json_string(); 387 | 388 | person__error_is_not_array(); 389 | 390 | person__error_unknown_field(); 391 | 392 | person__error_unknown_field_nested(); 393 | 394 | repeated_values__error_is_not_string_required_for_string(); 395 | 396 | read_file_success(); 397 | 398 | bytes_encode(); 399 | 400 | return 0; 401 | } 402 | -------------------------------------------------------------------------------- /test/runner-unix.c: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #include "runner-unix.h" 23 | #include "runner.h" 24 | 25 | #include /* uintptr_t */ 26 | 27 | #include 28 | #include /* usleep */ 29 | #include /* strdup */ 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | 41 | 42 | /* Do platform-specific initialization. */ 43 | void platform_init(int argc, char **argv) { 44 | const char* tap; 45 | 46 | tap = getenv("UV_TAP_OUTPUT"); 47 | tap_output = (tap != NULL && atoi(tap) > 0); 48 | 49 | /* Disable stdio output buffering. */ 50 | setvbuf(stdout, NULL, _IONBF, 0); 51 | setvbuf(stderr, NULL, _IONBF, 0); 52 | strncpy(executable_path, argv[0], sizeof(executable_path) - 1); 53 | signal(SIGPIPE, SIG_IGN); 54 | } 55 | 56 | 57 | /* Invoke "argv[0] test-name [test-part]". Store process info in *p. */ 58 | /* Make sure that all stdio output of the processes is buffered up. */ 59 | int process_start(char* name, char* part, process_info_t* p, int is_helper) { 60 | FILE* stdout_file; 61 | const char* arg; 62 | char* args[16]; 63 | int n; 64 | 65 | stdout_file = tmpfile(); 66 | if (!stdout_file) { 67 | perror("tmpfile"); 68 | return -1; 69 | } 70 | 71 | p->terminated = 0; 72 | p->status = 0; 73 | 74 | pid_t pid = fork(); 75 | 76 | if (pid < 0) { 77 | perror("fork"); 78 | return -1; 79 | } 80 | 81 | if (pid == 0) { 82 | /* child */ 83 | arg = getenv("UV_USE_VALGRIND"); 84 | n = 0; 85 | 86 | /* Disable valgrind for helpers, it complains about helpers leaking memory. 87 | * They're killed after the test and as such never get a chance to clean up. 88 | */ 89 | if (is_helper == 0 && arg != NULL && atoi(arg) != 0) { 90 | args[n++] = "valgrind"; 91 | args[n++] = "--quiet"; 92 | args[n++] = "--leak-check=full"; 93 | args[n++] = "--show-reachable=yes"; 94 | args[n++] = "--error-exitcode=125"; 95 | } 96 | 97 | args[n++] = executable_path; 98 | args[n++] = name; 99 | args[n++] = part; 100 | args[n++] = NULL; 101 | 102 | dup2(fileno(stdout_file), STDOUT_FILENO); 103 | dup2(fileno(stdout_file), STDERR_FILENO); 104 | execvp(args[0], args); 105 | perror("execvp()"); 106 | _exit(127); 107 | } 108 | 109 | /* parent */ 110 | p->pid = pid; 111 | p->name = strdup(name); 112 | p->stdout_file = stdout_file; 113 | 114 | return 0; 115 | } 116 | 117 | 118 | typedef struct { 119 | int pipe[2]; 120 | process_info_t* vec; 121 | int n; 122 | } dowait_args; 123 | 124 | 125 | /* This function is run inside a pthread. We do this so that we can possibly 126 | * timeout. 127 | */ 128 | static void* dowait(void* data) { 129 | dowait_args* args = data; 130 | 131 | int i, r; 132 | process_info_t* p; 133 | 134 | for (i = 0; i < args->n; i++) { 135 | p = (process_info_t*)(args->vec + i * sizeof(process_info_t)); 136 | if (p->terminated) continue; 137 | r = waitpid(p->pid, &p->status, 0); 138 | if (r < 0) { 139 | perror("waitpid"); 140 | return NULL; 141 | } 142 | p->terminated = 1; 143 | } 144 | 145 | if (args->pipe[1] >= 0) { 146 | /* Write a character to the main thread to notify it about this. */ 147 | ssize_t r; 148 | 149 | do 150 | r = write(args->pipe[1], "", 1); 151 | while (r == -1 && errno == EINTR); 152 | } 153 | 154 | return NULL; 155 | } 156 | 157 | 158 | /* Wait for all `n` processes in `vec` to terminate. */ 159 | /* Time out after `timeout` msec, or never if timeout == -1 */ 160 | /* Return 0 if all processes are terminated, -1 on error, -2 on timeout. */ 161 | int process_wait(process_info_t* vec, int n, int timeout) { 162 | int i; 163 | process_info_t* p; 164 | dowait_args args; 165 | args.vec = vec; 166 | args.n = n; 167 | args.pipe[0] = -1; 168 | args.pipe[1] = -1; 169 | 170 | /* The simple case is where there is no timeout */ 171 | if (timeout == -1) { 172 | dowait(&args); 173 | return 0; 174 | } 175 | 176 | /* Hard case. Do the wait with a timeout. 177 | * 178 | * Assumption: we are the only ones making this call right now. Otherwise 179 | * we'd need to lock vec. 180 | */ 181 | 182 | pthread_t tid; 183 | int retval; 184 | 185 | int r = pipe((int*)&(args.pipe)); 186 | if (r) { 187 | perror("pipe()"); 188 | return -1; 189 | } 190 | 191 | r = pthread_create(&tid, NULL, dowait, &args); 192 | if (r) { 193 | perror("pthread_create()"); 194 | retval = -1; 195 | goto terminate; 196 | } 197 | 198 | struct timeval tv; 199 | tv.tv_sec = timeout / 1000; 200 | tv.tv_usec = 0; 201 | 202 | fd_set fds; 203 | FD_ZERO(&fds); 204 | FD_SET(args.pipe[0], &fds); 205 | 206 | r = select(args.pipe[0] + 1, &fds, NULL, NULL, &tv); 207 | 208 | if (r == -1) { 209 | perror("select()"); 210 | retval = -1; 211 | } else if (r) { 212 | /* The thread completed successfully. */ 213 | retval = 0; 214 | } else { 215 | /* Timeout. Kill all the children. */ 216 | for (i = 0; i < n; i++) { 217 | p = (process_info_t*)(vec + i * sizeof(process_info_t)); 218 | kill(p->pid, SIGTERM); 219 | } 220 | retval = -2; 221 | 222 | /* Wait for thread to finish. */ 223 | r = pthread_join(tid, NULL); 224 | if (r) { 225 | perror("pthread_join"); 226 | retval = -1; 227 | } 228 | } 229 | 230 | terminate: 231 | 232 | r = pthread_detach(tid); 233 | if (r) { 234 | perror("pthread_detach()"); 235 | return -1; 236 | } 237 | 238 | close(args.pipe[0]); 239 | close(args.pipe[1]); 240 | return retval; 241 | } 242 | 243 | 244 | /* Returns the number of bytes in the stdio output buffer for process `p`. */ 245 | long int process_output_size(process_info_t *p) { 246 | /* Size of the p->stdout_file */ 247 | struct stat buf; 248 | 249 | int r = fstat(fileno(p->stdout_file), &buf); 250 | if (r < 0) { 251 | return -1; 252 | } 253 | 254 | return (long)buf.st_size; 255 | } 256 | 257 | 258 | /* Copy the contents of the stdio output buffer to `fd`. */ 259 | int process_copy_output(process_info_t *p, int fd) { 260 | int r = fseek(p->stdout_file, 0, SEEK_SET); 261 | if (r < 0) { 262 | perror("fseek"); 263 | return -1; 264 | } 265 | 266 | ssize_t nwritten; 267 | char buf[1024]; 268 | 269 | /* TODO: what if the line is longer than buf */ 270 | while (fgets(buf, sizeof(buf), p->stdout_file) != NULL) { 271 | /* TODO: what if write doesn't write the whole buffer... */ 272 | nwritten = 0; 273 | 274 | if (tap_output) 275 | nwritten += write(fd, "#", 1); 276 | 277 | nwritten += write(fd, buf, strlen(buf)); 278 | 279 | if (nwritten < 0) { 280 | perror("write"); 281 | return -1; 282 | } 283 | } 284 | 285 | if (ferror(p->stdout_file)) { 286 | perror("read"); 287 | return -1; 288 | } 289 | 290 | return 0; 291 | } 292 | 293 | 294 | /* Copy the last line of the stdio output buffer to `buffer` */ 295 | int process_read_last_line(process_info_t *p, 296 | char* buffer, 297 | size_t buffer_len) { 298 | char* ptr; 299 | 300 | int r = fseek(p->stdout_file, 0, SEEK_SET); 301 | if (r < 0) { 302 | perror("fseek"); 303 | return -1; 304 | } 305 | 306 | buffer[0] = '\0'; 307 | 308 | while (fgets(buffer, buffer_len, p->stdout_file) != NULL) { 309 | for (ptr = buffer; *ptr && *ptr != '\r' && *ptr != '\n'; ptr++); 310 | *ptr = '\0'; 311 | } 312 | 313 | if (ferror(p->stdout_file)) { 314 | perror("read"); 315 | buffer[0] = '\0'; 316 | return -1; 317 | } 318 | return 0; 319 | } 320 | 321 | 322 | /* Return the name that was specified when `p` was started by process_start */ 323 | char* process_get_name(process_info_t *p) { 324 | return p->name; 325 | } 326 | 327 | 328 | /* Terminate process `p`. */ 329 | int process_terminate(process_info_t *p) { 330 | return kill(p->pid, SIGTERM); 331 | } 332 | 333 | 334 | /* Return the exit code of process p. */ 335 | /* On error, return -1. */ 336 | int process_reap(process_info_t *p) { 337 | if (WIFEXITED(p->status)) { 338 | return WEXITSTATUS(p->status); 339 | } else { 340 | return p->status; /* ? */ 341 | } 342 | } 343 | 344 | 345 | /* Clean up after terminating process `p` (e.g. free the output buffer etc.). */ 346 | void process_cleanup(process_info_t *p) { 347 | fclose(p->stdout_file); 348 | free(p->name); 349 | } 350 | 351 | 352 | /* Move the console cursor one line up and back to the first column. */ 353 | void rewind_cursor(void) { 354 | fprintf(stderr, "\033[2K\r"); 355 | } 356 | 357 | 358 | /* Pause the calling thread for a number of milliseconds. */ 359 | void runner_sleep(int msec) { 360 | usleep(msec * 1000); 361 | } 362 | -------------------------------------------------------------------------------- /test/runner-unix.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #ifndef TEST_RUNNER_UNIX_H 23 | #define TEST_RUNNER_UNIX_H 24 | 25 | #include 26 | #include /* FILE */ 27 | 28 | typedef struct { 29 | FILE* stdout_file; 30 | pid_t pid; 31 | char* name; 32 | int status; 33 | int terminated; 34 | } process_info_t; 35 | 36 | #endif /* TEST_RUNNER_UNIX_H */ 37 | -------------------------------------------------------------------------------- /test/runner-win.c: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #if !defined(__MINGW32__) 28 | # include 29 | #endif 30 | 31 | 32 | #include "task.h" 33 | #include "runner.h" 34 | 35 | 36 | /* 37 | * Define the stuff that MinGW doesn't have 38 | */ 39 | #ifndef GetFileSizeEx 40 | WINBASEAPI BOOL WINAPI GetFileSizeEx(HANDLE hFile, 41 | PLARGE_INTEGER lpFileSize); 42 | #endif 43 | 44 | 45 | /* Do platform-specific initialization. */ 46 | void platform_init(int argc, char **argv) { 47 | const char* tap; 48 | 49 | tap = getenv("UV_TAP_OUTPUT"); 50 | tap_output = (tap != NULL && atoi(tap) > 0); 51 | 52 | /* Disable the "application crashed" popup. */ 53 | SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | 54 | SEM_NOOPENFILEERRORBOX); 55 | #if !defined(__MINGW32__) 56 | _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); 57 | _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); 58 | #endif 59 | 60 | _setmode(0, _O_BINARY); 61 | _setmode(1, _O_BINARY); 62 | _setmode(2, _O_BINARY); 63 | 64 | /* Disable stdio output buffering. */ 65 | setvbuf(stdout, NULL, _IONBF, 0); 66 | setvbuf(stderr, NULL, _IONBF, 0); 67 | 68 | strcpy(executable_path, argv[0]); 69 | } 70 | 71 | 72 | int process_start(char *name, char *part, process_info_t *p, int is_helper) { 73 | HANDLE file = INVALID_HANDLE_VALUE; 74 | HANDLE nul = INVALID_HANDLE_VALUE; 75 | WCHAR path[MAX_PATH], filename[MAX_PATH]; 76 | WCHAR image[MAX_PATH + 1]; 77 | WCHAR args[MAX_PATH * 2]; 78 | STARTUPINFOW si; 79 | PROCESS_INFORMATION pi; 80 | DWORD result; 81 | 82 | if (GetTempPathW(sizeof(path) / sizeof(WCHAR), (WCHAR*)&path) == 0) 83 | goto error; 84 | if (GetTempFileNameW((WCHAR*)&path, L"uv", 0, (WCHAR*)&filename) == 0) 85 | goto error; 86 | 87 | file = CreateFileW((WCHAR*)filename, 88 | GENERIC_READ | GENERIC_WRITE, 89 | 0, 90 | NULL, 91 | CREATE_ALWAYS, 92 | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, 93 | NULL); 94 | if (file == INVALID_HANDLE_VALUE) 95 | goto error; 96 | 97 | if (!SetHandleInformation(file, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) 98 | goto error; 99 | 100 | nul = CreateFileA("nul", 101 | GENERIC_READ, 102 | FILE_SHARE_READ | FILE_SHARE_WRITE, 103 | NULL, 104 | OPEN_EXISTING, 105 | FILE_ATTRIBUTE_NORMAL, 106 | NULL); 107 | if (nul == INVALID_HANDLE_VALUE) 108 | goto error; 109 | 110 | if (!SetHandleInformation(nul, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) 111 | goto error; 112 | 113 | result = GetModuleFileNameW(NULL, 114 | (WCHAR*) &image, 115 | sizeof(image) / sizeof(WCHAR)); 116 | if (result == 0 || result == sizeof(image)) 117 | goto error; 118 | 119 | if (part) { 120 | if (_snwprintf((WCHAR*)args, 121 | sizeof(args) / sizeof(WCHAR), 122 | L"\"%s\" %S %S", 123 | image, 124 | name, 125 | part) < 0) { 126 | goto error; 127 | } 128 | } else { 129 | if (_snwprintf((WCHAR*)args, 130 | sizeof(args) / sizeof(WCHAR), 131 | L"\"%s\" %S", 132 | image, 133 | name) < 0) { 134 | goto error; 135 | } 136 | } 137 | 138 | memset((void*)&si, 0, sizeof(si)); 139 | si.cb = sizeof(si); 140 | si.dwFlags = STARTF_USESTDHANDLES; 141 | si.hStdInput = nul; 142 | si.hStdOutput = file; 143 | si.hStdError = file; 144 | 145 | if (!CreateProcessW(image, args, NULL, NULL, TRUE, 146 | 0, NULL, NULL, &si, &pi)) 147 | goto error; 148 | 149 | CloseHandle(pi.hThread); 150 | 151 | SetHandleInformation(nul, HANDLE_FLAG_INHERIT, 0); 152 | SetHandleInformation(file, HANDLE_FLAG_INHERIT, 0); 153 | 154 | p->stdio_in = nul; 155 | p->stdio_out = file; 156 | p->process = pi.hProcess; 157 | p->name = part; 158 | 159 | return 0; 160 | 161 | error: 162 | if (file != INVALID_HANDLE_VALUE) 163 | CloseHandle(file); 164 | if (nul != INVALID_HANDLE_VALUE) 165 | CloseHandle(nul); 166 | 167 | return -1; 168 | } 169 | 170 | 171 | /* Timeout is is msecs. Set timeout < 0 to never time out. */ 172 | /* Returns 0 when all processes are terminated, -2 on timeout. */ 173 | int process_wait(process_info_t *vec, int n, int timeout) { 174 | int i; 175 | HANDLE handles[MAXIMUM_WAIT_OBJECTS]; 176 | DWORD timeout_api, result; 177 | 178 | /* If there's nothing to wait for, return immediately. */ 179 | if (n == 0) 180 | return 0; 181 | 182 | ASSERT(n <= MAXIMUM_WAIT_OBJECTS); 183 | 184 | for (i = 0; i < n; i++) 185 | handles[i] = vec[i].process; 186 | 187 | if (timeout >= 0) { 188 | timeout_api = (DWORD)timeout; 189 | } else { 190 | timeout_api = INFINITE; 191 | } 192 | 193 | result = WaitForMultipleObjects(n, handles, TRUE, timeout_api); 194 | 195 | if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + n) { 196 | /* All processes are terminated. */ 197 | return 0; 198 | } 199 | if (result == WAIT_TIMEOUT) { 200 | return -2; 201 | } 202 | return -1; 203 | } 204 | 205 | 206 | long int process_output_size(process_info_t *p) { 207 | LARGE_INTEGER size; 208 | if (!GetFileSizeEx(p->stdio_out, &size)) 209 | return -1; 210 | return (long int)size.QuadPart; 211 | } 212 | 213 | 214 | int process_copy_output(process_info_t *p, int fd) { 215 | DWORD read; 216 | char buf[1024]; 217 | char *line, *start; 218 | 219 | if (SetFilePointer(p->stdio_out, 220 | 0, 221 | 0, 222 | FILE_BEGIN) == INVALID_SET_FILE_POINTER) { 223 | return -1; 224 | } 225 | 226 | if (tap_output) 227 | write(fd, "#", 1); 228 | 229 | while (ReadFile(p->stdio_out, (void*)&buf, sizeof(buf), &read, NULL) && 230 | read > 0) { 231 | if (tap_output) { 232 | start = buf; 233 | 234 | while ((line = strchr(start, '\n')) != NULL) { 235 | write(fd, start, line - start + 1); 236 | write(fd, "#", 1); 237 | start = line + 1; 238 | } 239 | 240 | if (start < buf + read) 241 | write(fd, start, buf + read - start); 242 | } else { 243 | write(fd, buf, read); 244 | } 245 | } 246 | 247 | if (tap_output) 248 | write(fd, "\n", 1); 249 | 250 | if (GetLastError() != ERROR_HANDLE_EOF) 251 | return -1; 252 | 253 | return 0; 254 | } 255 | 256 | 257 | int process_read_last_line(process_info_t *p, 258 | char * buffer, 259 | size_t buffer_len) { 260 | DWORD size; 261 | DWORD read; 262 | DWORD start; 263 | OVERLAPPED overlapped; 264 | 265 | ASSERT(buffer_len > 0); 266 | 267 | size = GetFileSize(p->stdio_out, NULL); 268 | if (size == INVALID_FILE_SIZE) 269 | return -1; 270 | 271 | if (size == 0) { 272 | buffer[0] = '\0'; 273 | return 1; 274 | } 275 | 276 | memset(&overlapped, 0, sizeof overlapped); 277 | if (size >= buffer_len) 278 | overlapped.Offset = size - buffer_len - 1; 279 | 280 | if (!ReadFile(p->stdio_out, buffer, buffer_len - 1, &read, &overlapped)) 281 | return -1; 282 | 283 | for (start = read - 1; start >= 0; start--) { 284 | if (buffer[start] == '\n' || buffer[start] == '\r') 285 | break; 286 | } 287 | 288 | if (start > 0) 289 | memmove(buffer, buffer + start, read - start); 290 | 291 | buffer[read - start] = '\0'; 292 | 293 | return 0; 294 | } 295 | 296 | 297 | char* process_get_name(process_info_t *p) { 298 | return p->name; 299 | } 300 | 301 | 302 | int process_terminate(process_info_t *p) { 303 | if (!TerminateProcess(p->process, 1)) 304 | return -1; 305 | return 0; 306 | } 307 | 308 | 309 | int process_reap(process_info_t *p) { 310 | DWORD exitCode; 311 | if (!GetExitCodeProcess(p->process, &exitCode)) 312 | return -1; 313 | return (int)exitCode; 314 | } 315 | 316 | 317 | void process_cleanup(process_info_t *p) { 318 | CloseHandle(p->process); 319 | CloseHandle(p->stdio_in); 320 | CloseHandle(p->stdio_out); 321 | } 322 | 323 | 324 | static int clear_line() { 325 | HANDLE handle; 326 | CONSOLE_SCREEN_BUFFER_INFO info; 327 | COORD coord; 328 | DWORD written; 329 | 330 | handle = (HANDLE)_get_osfhandle(fileno(stderr)); 331 | if (handle == INVALID_HANDLE_VALUE) 332 | return -1; 333 | 334 | if (!GetConsoleScreenBufferInfo(handle, &info)) 335 | return -1; 336 | 337 | coord = info.dwCursorPosition; 338 | if (coord.Y <= 0) 339 | return -1; 340 | 341 | coord.X = 0; 342 | 343 | if (!SetConsoleCursorPosition(handle, coord)) 344 | return -1; 345 | 346 | if (!FillConsoleOutputCharacterW(handle, 347 | 0x20, 348 | info.dwSize.X, 349 | coord, 350 | &written)) { 351 | return -1; 352 | } 353 | 354 | return 0; 355 | } 356 | 357 | 358 | void rewind_cursor() { 359 | if (clear_line() == -1) { 360 | /* If clear_line fails (stdout is not a console), print a newline. */ 361 | fprintf(stderr, "\n"); 362 | } 363 | } 364 | 365 | 366 | /* Pause the calling thread for a number of milliseconds. */ 367 | void runner_sleep(int msec) { 368 | Sleep(msec); 369 | } 370 | -------------------------------------------------------------------------------- /test/runner-win.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | /* Don't complain about _snprintf being insecure. */ 23 | #define _CRT_SECURE_NO_WARNINGS 24 | 25 | /* Don't complain about write(), fileno() etc. being deprecated. */ 26 | #pragma warning(disable : 4996) 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | 34 | /* Windows has no snprintf, only _snprintf. */ 35 | #define snprintf _snprintf 36 | 37 | 38 | typedef struct { 39 | HANDLE process; 40 | HANDLE stdio_in; 41 | HANDLE stdio_out; 42 | char *name; 43 | } process_info_t; 44 | -------------------------------------------------------------------------------- /test/runner.c: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #include "runner.h" 26 | #include "task.h" 27 | 28 | char executable_path[MAXPATHLEN] = {0}; 29 | 30 | int tap_output = 0; 31 | 32 | 33 | static void log_progress(int total, 34 | int passed, 35 | int failed, 36 | int todos, 37 | int skipped, 38 | const char* name) { 39 | int progress; 40 | 41 | if (total == 0) 42 | total = 1; 43 | 44 | progress = 100 * (passed + failed + skipped + todos) / total; 45 | LOGF("[%% %3d|+ %3d|- %3d|T %3d|S %3d]: %s", 46 | progress, 47 | passed, 48 | failed, 49 | todos, 50 | skipped, 51 | name); 52 | } 53 | 54 | 55 | int run_tests(int benchmark_output) { 56 | int total; 57 | int passed; 58 | int failed; 59 | int todos; 60 | int skipped; 61 | int current; 62 | int test_result; 63 | task_entry_t* task; 64 | 65 | /* Count the number of tests. */ 66 | total = 0; 67 | for (task = TASKS; task->main; task++) { 68 | if (!task->is_helper) { 69 | total++; 70 | } 71 | } 72 | 73 | if (tap_output) { 74 | LOGF("1..%d\n", total); 75 | } 76 | 77 | /* Run all tests. */ 78 | passed = 0; 79 | failed = 0; 80 | todos = 0; 81 | skipped = 0; 82 | current = 1; 83 | for (task = TASKS; task->main; task++) { 84 | if (task->is_helper) { 85 | continue; 86 | } 87 | 88 | if (!tap_output) 89 | rewind_cursor(); 90 | 91 | if (!benchmark_output && !tap_output) { 92 | log_progress(total, passed, failed, todos, skipped, task->task_name); 93 | } 94 | 95 | test_result = run_test(task->task_name, benchmark_output, current); 96 | switch (test_result) { 97 | case TEST_OK: passed++; break; 98 | case TEST_TODO: todos++; break; 99 | case TEST_SKIP: skipped++; break; 100 | default: failed++; 101 | } 102 | current++; 103 | } 104 | 105 | if (!tap_output) 106 | rewind_cursor(); 107 | 108 | if (!benchmark_output && !tap_output) { 109 | log_progress(total, passed, failed, todos, skipped, "Done.\n"); 110 | } 111 | 112 | return failed; 113 | } 114 | 115 | 116 | void log_tap_result(int test_count, 117 | const char* test, 118 | int status, 119 | process_info_t* process) { 120 | const char* result; 121 | const char* directive; 122 | char reason[1024]; 123 | 124 | switch (status) { 125 | case TEST_OK: 126 | result = "ok"; 127 | directive = ""; 128 | break; 129 | case TEST_TODO: 130 | result = "not ok"; 131 | directive = " # TODO "; 132 | break; 133 | case TEST_SKIP: 134 | result = "ok"; 135 | directive = " # SKIP "; 136 | break; 137 | default: 138 | result = "not ok"; 139 | directive = ""; 140 | } 141 | 142 | if ((status == TEST_SKIP || status == TEST_TODO) && 143 | process_output_size(process) > 0) { 144 | process_read_last_line(process, reason, sizeof reason); 145 | } else { 146 | reason[0] = '\0'; 147 | } 148 | 149 | LOGF("%s %d - %s%s%s\n", result, test_count, test, directive, reason); 150 | } 151 | 152 | 153 | int run_test(const char* test, 154 | int benchmark_output, 155 | int test_count) { 156 | char errmsg[1024] = "no error"; 157 | process_info_t processes[1024]; 158 | process_info_t *main_proc; 159 | task_entry_t* task; 160 | int process_count; 161 | int result; 162 | int status; 163 | int i; 164 | 165 | status = 255; 166 | main_proc = NULL; 167 | process_count = 0; 168 | 169 | /* If it's a helper the user asks for, start it directly. */ 170 | for (task = TASKS; task->main; task++) { 171 | if (task->is_helper && strcmp(test, task->process_name) == 0) { 172 | return task->main(); 173 | } 174 | } 175 | 176 | /* Start the helpers first. */ 177 | for (task = TASKS; task->main; task++) { 178 | if (strcmp(test, task->task_name) != 0) { 179 | continue; 180 | } 181 | 182 | /* Skip the test itself. */ 183 | if (!task->is_helper) { 184 | continue; 185 | } 186 | 187 | if (process_start(task->task_name, 188 | task->process_name, 189 | &processes[process_count], 190 | 1 /* is_helper */) == -1) { 191 | snprintf(errmsg, 192 | sizeof errmsg, 193 | "Process `%s` failed to start.", 194 | task->process_name); 195 | goto out; 196 | } 197 | 198 | process_count++; 199 | } 200 | 201 | /* Give the helpers time to settle. Race-y, fix this. */ 202 | runner_sleep(250); 203 | 204 | /* Now start the test itself. */ 205 | for (task = TASKS; task->main; task++) { 206 | if (strcmp(test, task->task_name) != 0) { 207 | continue; 208 | } 209 | 210 | if (task->is_helper) { 211 | continue; 212 | } 213 | 214 | if (process_start(task->task_name, 215 | task->process_name, 216 | &processes[process_count], 217 | 0 /* !is_helper */) == -1) { 218 | snprintf(errmsg, 219 | sizeof errmsg, 220 | "Process `%s` failed to start.", 221 | task->process_name); 222 | goto out; 223 | } 224 | 225 | main_proc = &processes[process_count]; 226 | process_count++; 227 | break; 228 | } 229 | 230 | if (main_proc == NULL) { 231 | snprintf(errmsg, 232 | sizeof errmsg, 233 | "No test with that name: %s", 234 | test); 235 | goto out; 236 | } 237 | 238 | result = process_wait(main_proc, 1, task->timeout); 239 | if (result == -1) { 240 | FATAL("process_wait failed"); 241 | } else if (result == -2) { 242 | /* Don't have to clean up the process, process_wait() has killed it. */ 243 | snprintf(errmsg, 244 | sizeof errmsg, 245 | "timeout"); 246 | goto out; 247 | } 248 | 249 | status = process_reap(main_proc); 250 | if (status != TEST_OK) { 251 | snprintf(errmsg, 252 | sizeof errmsg, 253 | "exit code %d", 254 | status); 255 | goto out; 256 | } 257 | 258 | if (benchmark_output) { 259 | /* Give the helpers time to clean up their act. */ 260 | runner_sleep(1000); 261 | } 262 | 263 | out: 264 | /* Reap running processes except the main process, it's already dead. */ 265 | for (i = 0; i < process_count - 1; i++) { 266 | process_terminate(&processes[i]); 267 | } 268 | 269 | if (process_count > 0 && 270 | process_wait(processes, process_count - 1, -1) < 0) { 271 | FATAL("process_wait failed"); 272 | } 273 | 274 | if (tap_output) 275 | log_tap_result(test_count, test, status, &processes[i]); 276 | 277 | /* Show error and output from processes if the test failed. */ 278 | if (status != 0 || task->show_output) { 279 | if (tap_output) { 280 | LOGF("#"); 281 | } else if (status == TEST_TODO) { 282 | LOGF("\n`%s` todo\n", test); 283 | } else if (status == TEST_SKIP) { 284 | LOGF("\n`%s` skipped\n", test); 285 | } else if (status != 0) { 286 | LOGF("\n`%s` failed: %s\n", test, errmsg); 287 | } else { 288 | LOGF("\n"); 289 | } 290 | 291 | for (i = 0; i < process_count; i++) { 292 | switch (process_output_size(&processes[i])) { 293 | case -1: 294 | LOGF("Output from process `%s`: (unavailable)\n", 295 | process_get_name(&processes[i])); 296 | break; 297 | 298 | case 0: 299 | LOGF("Output from process `%s`: (no output)\n", 300 | process_get_name(&processes[i])); 301 | break; 302 | 303 | default: 304 | LOGF("Output from process `%s`:\n", process_get_name(&processes[i])); 305 | process_copy_output(&processes[i], fileno(stderr)); 306 | break; 307 | } 308 | } 309 | 310 | if (!tap_output) { 311 | LOG("=============================================================\n"); 312 | } 313 | 314 | /* In benchmark mode show concise output from the main process. */ 315 | } else if (benchmark_output) { 316 | switch (process_output_size(main_proc)) { 317 | case -1: 318 | LOGF("%s: (unavailable)\n", test); 319 | break; 320 | 321 | case 0: 322 | LOGF("%s: (no output)\n", test); 323 | break; 324 | 325 | default: 326 | for (i = 0; i < process_count; i++) { 327 | process_copy_output(&processes[i], fileno(stderr)); 328 | } 329 | break; 330 | } 331 | } 332 | 333 | /* Clean up all process handles. */ 334 | for (i = 0; i < process_count; i++) { 335 | process_cleanup(&processes[i]); 336 | } 337 | 338 | return status; 339 | } 340 | 341 | 342 | /* Returns the status code of the task part 343 | * or 255 if no matching task was not found. 344 | */ 345 | int run_test_part(const char* test, const char* part) { 346 | task_entry_t* task; 347 | int r; 348 | 349 | for (task = TASKS; task->main; task++) { 350 | if (strcmp(test, task->task_name) == 0 && 351 | strcmp(part, task->process_name) == 0) { 352 | r = task->main(); 353 | return r; 354 | } 355 | } 356 | 357 | LOGF("No test part with that name: %s:%s\n", test, part); 358 | return 255; 359 | } 360 | 361 | 362 | static int compare_task(const void* va, const void* vb) { 363 | const task_entry_t* a = va; 364 | const task_entry_t* b = vb; 365 | return strcmp(a->task_name, b->task_name); 366 | } 367 | 368 | 369 | static int find_helpers(const task_entry_t* task, 370 | const task_entry_t** helpers) { 371 | const task_entry_t* helper; 372 | int n_helpers; 373 | 374 | for (n_helpers = 0, helper = TASKS; helper->main; helper++) { 375 | if (helper->is_helper && strcmp(helper->task_name, task->task_name) == 0) { 376 | *helpers++ = helper; 377 | n_helpers++; 378 | } 379 | } 380 | 381 | return n_helpers; 382 | } 383 | 384 | 385 | void print_tests() { 386 | const task_entry_t* helpers[1024]; 387 | const task_entry_t* task; 388 | int n_helpers; 389 | int n_tasks; 390 | int i; 391 | 392 | for (n_tasks = 0, task = TASKS; task->main; n_tasks++, task++); 393 | qsort(TASKS, n_tasks, sizeof(TASKS[0]), compare_task); 394 | 395 | for (task = TASKS; task->main; task++) { 396 | if (task->is_helper) { 397 | continue; 398 | } 399 | 400 | n_helpers = find_helpers(task, helpers); 401 | if (n_helpers) { 402 | printf("%-25s (helpers:", task->task_name); 403 | for (i = 0; i < n_helpers; i++) { 404 | printf(" %s", helpers[i]->process_name); 405 | } 406 | printf(")\n"); 407 | } else { 408 | printf("%s\n", task->task_name); 409 | } 410 | } 411 | } 412 | 413 | 414 | void print_help(const char* argv0) { 415 | printf("Runner based in libuv tests runner.\n"); 416 | printf("\n"); 417 | printf("Usage:\n"); 418 | printf(" $> %s # run all tasks\n", argv0); 419 | printf(" $> %s # run only , starts up any helpers\n", argv0); 420 | printf(" $> %s # run only of \n", argv0); 421 | printf(" $> %s (-l|--list) # list all tasks\n", argv0); 422 | printf(" $> %s (-h|--help) # show this help message\n", argv0); 423 | printf("\n"); 424 | } 425 | -------------------------------------------------------------------------------- /test/runner.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #ifndef RUNNER_H_ 23 | #define RUNNER_H_ 24 | 25 | #include /* FILE */ 26 | 27 | #include "task.h" 28 | 29 | 30 | /* 31 | * The maximum number of processes (main + helpers) that a test / benchmark 32 | * can have. 33 | */ 34 | #define MAX_PROCESSES 8 35 | 36 | 37 | /* 38 | * Struct to store both tests and to define helper processes for tasks. 39 | */ 40 | typedef struct { 41 | char *task_name; 42 | char *process_name; 43 | int (*main)(void); 44 | int is_helper; 45 | int show_output; 46 | 47 | /* 48 | * The time in milliseconds after which a single test or benchmark times out. 49 | */ 50 | int timeout; 51 | } task_entry_t, bench_entry_t; 52 | 53 | 54 | /* 55 | * Macros used by test-list.h and benchmark-list.h. 56 | */ 57 | #define TASK_LIST_START \ 58 | task_entry_t TASKS[] = { 59 | 60 | #define TASK_LIST_END \ 61 | { 0, 0, 0, 0, 0, 0 } \ 62 | }; 63 | 64 | #define TEST_DECLARE(name) \ 65 | int run_test_##name(void); 66 | 67 | #define TEST_ENTRY(name) \ 68 | { #name, #name, &run_test_##name, 0, 0, 5000 }, 69 | 70 | #define TEST_ENTRY_CUSTOM(name, is_helper, show_output, timeout) \ 71 | { #name, #name, &run_test_##name, is_helper, show_output, timeout }, 72 | 73 | #define BENCHMARK_DECLARE(name) \ 74 | int run_benchmark_##name(void); 75 | 76 | #define BENCHMARK_ENTRY(name) \ 77 | { #name, #name, &run_benchmark_##name, 0, 0, 60000 }, 78 | 79 | #define HELPER_DECLARE(name) \ 80 | int run_helper_##name(void); 81 | 82 | #define HELPER_ENTRY(task_name, name) \ 83 | { #task_name, #name, &run_helper_##name, 1, 0, 0 }, 84 | 85 | #define TEST_HELPER HELPER_ENTRY 86 | #define BENCHMARK_HELPER HELPER_ENTRY 87 | 88 | extern char executable_path[MAXPATHLEN]; 89 | 90 | /* 91 | * Include platform-dependent definitions 92 | */ 93 | #ifdef _WIN32 94 | # include "runner-win.h" 95 | #else 96 | # include "runner-unix.h" 97 | #endif 98 | 99 | 100 | /* The array that is filled by test-list.h or benchmark-list.h */ 101 | extern task_entry_t TASKS[]; 102 | 103 | /* 104 | * Run all tests. 105 | */ 106 | int run_tests(int benchmark_output); 107 | 108 | /* 109 | * Run a single test. Starts up any helpers. 110 | */ 111 | int run_test(const char* test, 112 | int benchmark_output, 113 | int test_count); 114 | 115 | /* 116 | * Run a test part, i.e. the test or one of its helpers. 117 | */ 118 | int run_test_part(const char* test, const char* part); 119 | 120 | 121 | /* 122 | * Print tests in sorted order. 123 | */ 124 | void print_tests(); 125 | 126 | 127 | /* 128 | * Print help. 129 | */ 130 | void print_help(const char* argv0); 131 | 132 | 133 | /* 134 | * Stuff that should be implemented by test-runner-.h 135 | * All functions return 0 on success, -1 on failure, unless specified 136 | * otherwise. 137 | */ 138 | 139 | /* Do platform-specific initialization. */ 140 | void platform_init(int argc, char** argv); 141 | 142 | /* Invoke "argv[0] test-name [test-part]". Store process info in *p. */ 143 | /* Make sure that all stdio output of the processes is buffered up. */ 144 | int process_start(char *name, char* part, process_info_t *p, int is_helper); 145 | 146 | /* Wait for all `n` processes in `vec` to terminate. */ 147 | /* Time out after `timeout` msec, or never if timeout == -1 */ 148 | /* Return 0 if all processes are terminated, -1 on error, -2 on timeout. */ 149 | int process_wait(process_info_t *vec, int n, int timeout); 150 | 151 | /* Returns the number of bytes in the stdio output buffer for process `p`. */ 152 | long int process_output_size(process_info_t *p); 153 | 154 | /* Copy the contents of the stdio output buffer to `fd`. */ 155 | int process_copy_output(process_info_t *p, int fd); 156 | 157 | /* Copy the last line of the stdio output buffer to `buffer` */ 158 | int process_read_last_line(process_info_t *p, 159 | char * buffer, 160 | size_t buffer_len); 161 | 162 | /* Return the name that was specified when `p` was started by process_start */ 163 | char* process_get_name(process_info_t *p); 164 | 165 | /* Terminate process `p`. */ 166 | int process_terminate(process_info_t *p); 167 | 168 | /* Return the exit code of process p. */ 169 | /* On error, return -1. */ 170 | int process_reap(process_info_t *p); 171 | 172 | /* Clean up after terminating process `p` (e.g. free the output buffer etc.). */ 173 | void process_cleanup(process_info_t *p); 174 | 175 | /* Move the console cursor one line up and back to the first column. */ 176 | void rewind_cursor(void); 177 | 178 | /* trigger output as tap */ 179 | extern int tap_output; 180 | 181 | #endif /* RUNNER_H_ */ 182 | -------------------------------------------------------------------------------- /test/task.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | 22 | #ifndef TASK_H_ 23 | #define TASK_H_ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #if defined(_MSC_VER) && _MSC_VER < 1600 32 | # include "stdint-msvc2008.h" 33 | #else 34 | # include 35 | #endif 36 | 37 | #if !defined(_WIN32) 38 | # include 39 | # include /* setrlimit() */ 40 | #endif 41 | 42 | #ifdef _WIN32 43 | # include 44 | # ifndef S_IRUSR 45 | # define S_IRUSR _S_IREAD 46 | # endif 47 | # ifndef S_IWUSR 48 | # define S_IWUSR _S_IWRITE 49 | # endif 50 | #endif 51 | 52 | #ifndef MAXPATHLEN 53 | # ifdef PATH_MAX 54 | # define MAXPATHLEN PATH_MAX 55 | # elif defined(_MAX_PATH) 56 | # define MAXPATHLEN _MAX_PATH 57 | # elif defined(CCHMAXPATH) 58 | # define MAXPATHLEN CCHMAXPATH 59 | # else 60 | # define MAXPATHLEN 1024 61 | # endif 62 | #endif 63 | 64 | #define TEST_JSON_FLAGS (JSON_INDENT(2) | JSON_PRESERVE_ORDER) 65 | 66 | #define container_of(ptr, type, member) \ 67 | ((type *) ((char *) (ptr) - offsetof(type, member))) 68 | 69 | /* Log to stderr. */ 70 | #define LOG(...) \ 71 | do { \ 72 | fprintf(stderr, "%s", __VA_ARGS__); \ 73 | fflush(stderr); \ 74 | } while (0) 75 | 76 | #define LOGF(...) \ 77 | do { \ 78 | fprintf(stderr, __VA_ARGS__); \ 79 | fflush(stderr); \ 80 | } while (0) 81 | 82 | /* Die with fatal error. */ 83 | #define FATAL(msg) \ 84 | do { \ 85 | fprintf(stderr, \ 86 | "Fatal error in %s on line %d: %s\n", \ 87 | __FILE__, \ 88 | __LINE__, \ 89 | msg); \ 90 | fflush(stderr); \ 91 | abort(); \ 92 | } while (0) 93 | 94 | /* Have our own assert, so we are sure it does not get optimized away in 95 | * a release build. 96 | */ 97 | #define ASSERT(expr) \ 98 | do { \ 99 | if (!(expr)) { \ 100 | fprintf(stderr, \ 101 | "Assertion failed in %s on line %d: %s\n", \ 102 | __FILE__, \ 103 | __LINE__, \ 104 | #expr); \ 105 | abort(); \ 106 | } \ 107 | } while (0) 108 | 109 | #define ASSERT_ZERO(expr) \ 110 | do { \ 111 | if (expr != 0) { \ 112 | fprintf(stderr, \ 113 | "Assertion failed in %s on line %d: " \ 114 | "%s is not zero, but %d\n", \ 115 | __FILE__, \ 116 | __LINE__, \ 117 | #expr, expr); \ 118 | abort(); \ 119 | } \ 120 | } while (0) 121 | 122 | #define ASSERT_EQUALS(actual, expected) \ 123 | do { \ 124 | if (actual != expected) { \ 125 | fprintf(stderr, \ 126 | "Assertion failed in %s on line %d: " \ 127 | "%s is not %d, but %d\n", \ 128 | __FILE__, \ 129 | __LINE__, \ 130 | #actual, expected, actual); \ 131 | abort(); \ 132 | } \ 133 | } while (0) 134 | 135 | /* Assert that string are equal using strcmp */ 136 | #define ASSERT_STRCMP(actual, expected) \ 137 | do { \ 138 | if (strcmp(actual, expected)) { \ 139 | fprintf(stderr, \ 140 | "Assertion failed in %s on line %d:\n%s\n --- not equal to expected --- \n%s\n", \ 141 | __FILE__, \ 142 | __LINE__, \ 143 | actual, \ 144 | expected); \ 145 | abort(); \ 146 | } \ 147 | } while (0) 148 | 149 | /* Assert that string are equal using strncmp */ 150 | #define ASSERT_STRNCMP(actual, expected, length) \ 151 | do { \ 152 | if (strncmp(actual, expected, length)) { \ 153 | fprintf(stderr, \ 154 | "Assertion failed in %s on line %d:\n%s\n --- not equal to expected --- \n%s\n", \ 155 | __FILE__, \ 156 | __LINE__, \ 157 | actual, \ 158 | expected); \ 159 | abort(); \ 160 | } \ 161 | } while (0) 162 | 163 | 164 | /* Just sugar for wrapping the main() for a task or helper. */ 165 | #define TEST_IMPL(name) \ 166 | int run_test_##name(void); \ 167 | int run_test_##name(void) 168 | 169 | #define BENCHMARK_IMPL(name) \ 170 | int run_benchmark_##name(void); \ 171 | int run_benchmark_##name(void) 172 | 173 | #define HELPER_IMPL(name) \ 174 | int run_helper_##name(void); \ 175 | int run_helper_##name(void) 176 | 177 | /* Pause the calling thread for a number of milliseconds. */ 178 | void runner_sleep(int msec); 179 | 180 | /* Reserved test exit codes. */ 181 | enum test_status { 182 | TEST_OK = 0, 183 | TEST_TODO, 184 | TEST_SKIP 185 | }; 186 | 187 | #define RETURN_OK() \ 188 | do { \ 189 | return TEST_OK; \ 190 | } while (0) 191 | 192 | #define RETURN_TODO(explanation) \ 193 | do { \ 194 | LOGF("%s\n", explanation); \ 195 | return TEST_TODO; \ 196 | } while (0) 197 | 198 | #define RETURN_SKIP(explanation) \ 199 | do { \ 200 | LOGF("%s\n", explanation); \ 201 | return TEST_SKIP; \ 202 | } while (0) 203 | 204 | #if defined _WIN32 && ! defined __GNUC__ 205 | 206 | #include 207 | 208 | /* Emulate snprintf() on Windows, _snprintf() doesn't zero-terminate the buffer 209 | * on overflow... 210 | */ 211 | static int snprintf(char* buf, size_t len, const char* fmt, ...) { 212 | va_list ap; 213 | int n; 214 | 215 | va_start(ap, fmt); 216 | n = _vsprintf_p(buf, len, fmt, ap); 217 | va_end(ap); 218 | 219 | /* It's a sad fact of life that no one ever checks the return value of 220 | * snprintf(). Zero-terminating the buffer hopefully reduces the risk 221 | * of gaping security holes. 222 | */ 223 | if (n < 0) 224 | if (len > 0) 225 | buf[0] = '\0'; 226 | 227 | return n; 228 | } 229 | 230 | #endif 231 | 232 | #endif /* TASK_H_ */ 233 | -------------------------------------------------------------------------------- /test/test-json2protobuf-file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #include "task.h" 10 | #include "test.pb-c.h" 11 | #include "protobuf2json.h" 12 | 13 | #include 14 | 15 | extern char executable_path[MAXPATHLEN]; 16 | 17 | TEST_IMPL(json2protobuf_file__success) { 18 | int result; 19 | 20 | char file_path[MAXPATHLEN] = {0}; 21 | char file_name[MAXPATHLEN] = {0}; 22 | 23 | result = realpath(dirname(executable_path), file_path) ? 1 : 0; 24 | ASSERT(result > 0); 25 | 26 | result = snprintf(file_name, sizeof(file_name) - 1, "%s/fixtures/good.json", file_path); 27 | ASSERT(result > 0); 28 | 29 | ProtobufCMessage *protobuf_message = NULL; 30 | 31 | result = json2protobuf_file((char *)file_name, 0, &foo__person__descriptor, &protobuf_message, NULL, 0); 32 | ASSERT_ZERO(result); 33 | 34 | char *json_string; 35 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 36 | ASSERT_ZERO(result); 37 | ASSERT(json_string); 38 | 39 | ASSERT_STRCMP( 40 | json_string, 41 | "{\n" 42 | " \"name\": \"John Doe\",\n" 43 | " \"id\": 42\n" 44 | "}" 45 | ); 46 | 47 | free(json_string); 48 | 49 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 50 | 51 | RETURN_OK(); 52 | } 53 | 54 | TEST_IMPL(json2protobuf_file__error_cannot_parse_bad_message) { 55 | int result; 56 | char error_string[256] = {0}; 57 | 58 | char file_path[MAXPATHLEN] = {0}; 59 | char file_name[MAXPATHLEN] = {0}; 60 | 61 | result = realpath(dirname(executable_path), file_path) ? 1 : 0; 62 | ASSERT(result > 0); 63 | 64 | result = snprintf(file_name, sizeof(file_name) - 1, "%s/fixtures/bad_message.json", file_path); 65 | ASSERT(result > 0); 66 | 67 | ProtobufCMessage *protobuf_message = NULL; 68 | 69 | result = json2protobuf_file((char *)file_name, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 70 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_UNKNOWN_FIELD); 71 | 72 | const char *expected_error_string = \ 73 | "Unknown field 'unknown_field' for message 'Foo.Person'" 74 | ; 75 | 76 | ASSERT_STRCMP( 77 | error_string, 78 | expected_error_string 79 | ); 80 | 81 | RETURN_OK(); 82 | } 83 | 84 | TEST_IMPL(json2protobuf_file__error_cannot_parse_bad_json) { 85 | int result; 86 | char error_string[256] = {0}; 87 | 88 | char file_path[MAXPATHLEN] = {0}; 89 | char file_name[MAXPATHLEN] = {0}; 90 | 91 | result = realpath(dirname(executable_path), file_path) ? 1 : 0; 92 | ASSERT(result > 0); 93 | 94 | result = snprintf(file_name, sizeof(file_name) - 1, "%s/fixtures/bad_json.json", file_path); 95 | ASSERT(result > 0); 96 | 97 | ProtobufCMessage *protobuf_message = NULL; 98 | 99 | result = json2protobuf_file((char *)file_name, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 100 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_PARSE_FILE); 101 | 102 | const char *expected_error_string = \ 103 | "JSON parsing error at line 1 column 1 (position 1): " 104 | "'[' or '{' expected near '.'" 105 | ; 106 | 107 | ASSERT_STRCMP( 108 | error_string, 109 | expected_error_string 110 | ); 111 | 112 | RETURN_OK(); 113 | } 114 | 115 | TEST_IMPL(json2protobuf_file__error_cannot_parse_unexistent_file) { 116 | int result; 117 | char error_string[256] = {0}; 118 | 119 | char file_path[MAXPATHLEN] = {0}; 120 | char file_name[MAXPATHLEN] = {0}; 121 | 122 | result = realpath(dirname(executable_path), file_path) ? 1 : 0; 123 | ASSERT(result > 0); 124 | 125 | result = snprintf(file_name, sizeof(file_name) - 1, "%s/fixtures/unexistent.json", file_path); 126 | ASSERT(result > 0); 127 | 128 | ProtobufCMessage *protobuf_message = NULL; 129 | 130 | result = json2protobuf_file((char *)file_name, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 131 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_PARSE_FILE); 132 | 133 | const char *expected_error_string_beginning = \ 134 | "JSON parsing error at line -1 column -1 (position 0): " 135 | "unable to open" 136 | ; 137 | 138 | ASSERT(strstr(error_string, expected_error_string_beginning) == error_string); 139 | 140 | RETURN_OK(); 141 | } 142 | -------------------------------------------------------------------------------- /test/test-json2protobuf-string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #include "task.h" 10 | #include "test.pb-c.h" 11 | #include "protobuf2json.h" 12 | 13 | #include 14 | 15 | TEST_IMPL(json2protobuf_string__error_cannot_parse_wrong_string) { 16 | int result; 17 | char error_string[256] = {0}; 18 | 19 | const char *initial_json_string = "..."; 20 | 21 | ProtobufCMessage *protobuf_message = NULL; 22 | 23 | result = json2protobuf_string((char *)initial_json_string, TEST_JSON_FLAGS, &foo__bar__descriptor, &protobuf_message, error_string, sizeof(error_string)); 24 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_PARSE_STRING); 25 | 26 | const char *expected_error_string = \ 27 | "JSON parsing error at line 1 column 1 (position 1): " 28 | "'[' or '{' expected near '.'" 29 | ; 30 | 31 | ASSERT_STRCMP( 32 | error_string, 33 | expected_error_string 34 | ); 35 | 36 | RETURN_OK(); 37 | } 38 | 39 | TEST_IMPL(json2protobuf_string__error_duplicate_field) { 40 | int result; 41 | char error_string[256] = {0}; 42 | 43 | const char *initial_json_string = \ 44 | "{\n" 45 | " \"name\": \"John Doe\",\n" 46 | " \"id\": 42,\n" 47 | " \"name\": \"Jack Impostor\"\n" 48 | "}" 49 | ; 50 | 51 | ProtobufCMessage *protobuf_message = NULL; 52 | 53 | result = json2protobuf_string((char *)initial_json_string, JSON_REJECT_DUPLICATES, &foo__bar__descriptor, &protobuf_message, error_string, sizeof(error_string)); 54 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_PARSE_STRING); 55 | 56 | const char *expected_error_string = \ 57 | "JSON parsing error at line 4 column 8 (position 44): " 58 | "duplicate object key near '\"name\"'" 59 | ; 60 | 61 | ASSERT_STRCMP( 62 | error_string, 63 | expected_error_string 64 | ); 65 | 66 | RETURN_OK(); 67 | } 68 | 69 | TEST_IMPL(json2protobuf_string__error_is_not_array) { 70 | int result; 71 | char error_string[256] = {0}; 72 | 73 | const char *initial_json_string = \ 74 | "{\n" 75 | " \"name\": \"John Doe\",\n" 76 | " \"id\": 42,\n" 77 | " \"phone\": {}\n" 78 | "}" 79 | ; 80 | 81 | ProtobufCMessage *protobuf_message = NULL; 82 | 83 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 84 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_ARRAY); 85 | 86 | const char *expected_error_string = \ 87 | "JSON is not an array required for repeatable GPB field" 88 | ; 89 | 90 | ASSERT_STRCMP( 91 | error_string, 92 | expected_error_string 93 | ); 94 | 95 | RETURN_OK(); 96 | } 97 | 98 | TEST_IMPL(json2protobuf_string__error_unknown_field) { 99 | int result; 100 | char error_string[256] = {0}; 101 | 102 | const char *initial_json_string = \ 103 | "{\n" 104 | " \"unknown_field\": \"unknown_field_value\"\n" 105 | "}" 106 | ; 107 | 108 | ProtobufCMessage *protobuf_message = NULL; 109 | 110 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 111 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_UNKNOWN_FIELD); 112 | 113 | const char *expected_error_string = \ 114 | "Unknown field 'unknown_field' for message 'Foo.Person'" 115 | ; 116 | 117 | ASSERT_STRCMP( 118 | error_string, 119 | expected_error_string 120 | ); 121 | 122 | RETURN_OK(); 123 | } 124 | 125 | TEST_IMPL(json2protobuf_string__error_unknown_enum_value) { 126 | int result; 127 | char error_string[256] = {0}; 128 | 129 | const char *initial_json_string = \ 130 | "{\n" 131 | " \"name\": \"John Doe\",\n" 132 | " \"id\": 42,\n" 133 | " \"phone\": [\n" 134 | " {\n" 135 | " \"number\": \"+123456789\",\n" 136 | " \"type\": \"UNKNOWN\"\n" 137 | " }\n" 138 | " ]\n" 139 | "}" 140 | ; 141 | 142 | ProtobufCMessage *protobuf_message = NULL; 143 | 144 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 145 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_UNKNOWN_ENUM_VALUE); 146 | 147 | const char *expected_error_string = \ 148 | "Unknown value 'UNKNOWN' for enum 'Foo.Person.PhoneType'" 149 | ; 150 | 151 | ASSERT_STRCMP( 152 | error_string, 153 | expected_error_string 154 | ); 155 | 156 | RETURN_OK(); 157 | } 158 | 159 | TEST_IMPL(json2protobuf_string__error_required_is_missing) { 160 | int result; 161 | char error_string[256] = {0}; 162 | 163 | const char *initial_json_string = \ 164 | "{\n" 165 | " \"name\": \"John Doe\"\n" 166 | "}" 167 | ; 168 | 169 | ProtobufCMessage *protobuf_message = NULL; 170 | 171 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 172 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_REQUIRED_IS_MISSING); 173 | 174 | const char *expected_error_string = \ 175 | "Required field 'id' is missing in message 'Foo.Person'" 176 | ; 177 | 178 | ASSERT_STRCMP( 179 | error_string, 180 | expected_error_string 181 | ); 182 | 183 | RETURN_OK(); 184 | } 185 | 186 | TEST_IMPL(json2protobuf_string__error_is_not_object_required_for_message) { 187 | int result; 188 | char error_string[256] = {0}; 189 | 190 | const char *initial_json_string = \ 191 | "{\n" 192 | " \"name\": \"John Doe\",\n" 193 | " \"id\": 42,\n" 194 | " \"phone\": [[]]\n" 195 | "}" 196 | ; 197 | 198 | ProtobufCMessage *protobuf_message = NULL; 199 | 200 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, error_string, sizeof(error_string)); 201 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_OBJECT); 202 | 203 | const char *expected_error_string = \ 204 | "JSON is not an object required for GPB message" 205 | ; 206 | 207 | ASSERT_STRCMP( 208 | error_string, 209 | expected_error_string 210 | ); 211 | 212 | RETURN_OK(); 213 | } 214 | 215 | #define TEST_IMPL_IS_NOT_INTEGER(type) \ 216 | TEST_IMPL(json2protobuf_string__error_is_not_integer_required_for_ ## type) { \ 217 | int result; \ 218 | char error_string[256] = {0}; \ 219 | \ 220 | const char *initial_json_string = \ 221 | "{\n" \ 222 | " \"value_" #type "\": [\"string\"]\n" \ 223 | "}" \ 224 | ; \ 225 | \ 226 | ProtobufCMessage *protobuf_message = NULL; \ 227 | \ 228 | result = json2protobuf_string( \ 229 | (char *)initial_json_string, \ 230 | 0, \ 231 | &foo__repeated_values__descriptor, \ 232 | &protobuf_message, \ 233 | error_string, \ 234 | sizeof(error_string) \ 235 | ); \ 236 | \ 237 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_INTEGER); \ 238 | \ 239 | const char *expected_error_string = "JSON value is not an integer required for GPB " #type; \ 240 | ASSERT_STRCMP(error_string, expected_error_string); \ 241 | \ 242 | RETURN_OK(); \ 243 | } 244 | 245 | TEST_IMPL_IS_NOT_INTEGER(int32) 246 | TEST_IMPL_IS_NOT_INTEGER(sint32) 247 | TEST_IMPL_IS_NOT_INTEGER(sfixed32) 248 | TEST_IMPL_IS_NOT_INTEGER(uint32) 249 | TEST_IMPL_IS_NOT_INTEGER(fixed32) 250 | TEST_IMPL_IS_NOT_INTEGER(int64) 251 | TEST_IMPL_IS_NOT_INTEGER(sint64) 252 | TEST_IMPL_IS_NOT_INTEGER(sfixed64) 253 | TEST_IMPL_IS_NOT_INTEGER(uint64) 254 | TEST_IMPL_IS_NOT_INTEGER(fixed64) 255 | 256 | TEST_IMPL(json2protobuf_string__error_is_not_real_number_required_for_float) { 257 | int result; 258 | char error_string[256] = {0}; 259 | 260 | const char *initial_json_string = \ 261 | "{\n" 262 | " \"value_float\": [\"string\"]\n" 263 | "}" 264 | ; 265 | 266 | ProtobufCMessage *protobuf_message = NULL; 267 | 268 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, error_string, sizeof(error_string)); 269 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_INTEGER_OR_REAL); 270 | 271 | const char *expected_error_string = \ 272 | "JSON value is not a integer/real required for GPB float" 273 | ; 274 | 275 | ASSERT_STRCMP( 276 | error_string, 277 | expected_error_string 278 | ); 279 | 280 | RETURN_OK(); 281 | } 282 | 283 | TEST_IMPL(json2protobuf_string__error_is_not_real_number_required_for_double) { 284 | int result; 285 | char error_string[256] = {0}; 286 | 287 | const char *initial_json_string = \ 288 | "{\n" 289 | " \"value_double\": [\"string\"]\n" 290 | "}" 291 | ; 292 | 293 | ProtobufCMessage *protobuf_message = NULL; 294 | 295 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, error_string, sizeof(error_string)); 296 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_INTEGER_OR_REAL); 297 | 298 | const char *expected_error_string = \ 299 | "JSON value is not a integer/real required for GPB double" 300 | ; 301 | 302 | ASSERT_STRCMP( 303 | error_string, 304 | expected_error_string 305 | ); 306 | 307 | RETURN_OK(); 308 | } 309 | 310 | TEST_IMPL(json2protobuf_string__error_is_not_boolean_required_for_bool) { 311 | int result; 312 | char error_string[256] = {0}; 313 | 314 | const char *initial_json_string = \ 315 | "{\n" 316 | " \"value_bool\": [\"string\"]\n" 317 | "}" 318 | ; 319 | 320 | ProtobufCMessage *protobuf_message = NULL; 321 | 322 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, error_string, sizeof(error_string)); 323 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_BOOLEAN); 324 | 325 | const char *expected_error_string = \ 326 | "JSON value is not a boolean required for GPB bool" 327 | ; 328 | 329 | ASSERT_STRCMP( 330 | error_string, 331 | expected_error_string 332 | ); 333 | 334 | RETURN_OK(); 335 | } 336 | 337 | TEST_IMPL(json2protobuf_string__error_is_not_string_required_for_enum) { 338 | int result; 339 | char error_string[256] = {0}; 340 | 341 | const char *initial_json_string = \ 342 | "{\n" 343 | " \"value_enum\": [42]\n" 344 | "}" 345 | ; 346 | 347 | ProtobufCMessage *protobuf_message = NULL; 348 | 349 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, error_string, sizeof(error_string)); 350 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_STRING); 351 | 352 | const char *expected_error_string = \ 353 | "JSON value is not a string required for GPB enum" 354 | ; 355 | 356 | ASSERT_STRCMP( 357 | error_string, 358 | expected_error_string 359 | ); 360 | 361 | RETURN_OK(); 362 | } 363 | 364 | TEST_IMPL(json2protobuf_string__error_is_not_string_required_for_string) { 365 | int result; 366 | char error_string[256] = {0}; 367 | 368 | const char *initial_json_string = \ 369 | "{\n" 370 | " \"value_string\": [42]\n" 371 | "}" 372 | ; 373 | 374 | ProtobufCMessage *protobuf_message = NULL; 375 | 376 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, error_string, sizeof(error_string)); 377 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_STRING); 378 | 379 | const char *expected_error_string = \ 380 | "JSON value is not a string required for GPB string" 381 | ; 382 | 383 | ASSERT_STRCMP( 384 | error_string, 385 | expected_error_string 386 | ); 387 | 388 | RETURN_OK(); 389 | } 390 | 391 | TEST_IMPL(json2protobuf_string__error_is_not_string_required_for_bytes) { 392 | int result; 393 | char error_string[256] = {0}; 394 | 395 | const char *initial_json_string = \ 396 | "{\n" 397 | " \"value_bytes\": [42]\n" 398 | "}" 399 | ; 400 | 401 | ProtobufCMessage *protobuf_message = NULL; 402 | 403 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, error_string, sizeof(error_string)); 404 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_IS_NOT_STRING); 405 | 406 | const char *expected_error_string = \ 407 | "JSON value is not a string required for GPB bytes" 408 | ; 409 | 410 | ASSERT_STRCMP( 411 | error_string, 412 | expected_error_string 413 | ); 414 | 415 | RETURN_OK(); 416 | } 417 | -------------------------------------------------------------------------------- /test/test-list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | TEST_DECLARE(protobuf2json_file__success) 10 | TEST_DECLARE(protobuf2json_file__error_alloc) 11 | TEST_DECLARE(protobuf2json_file__error_cannot_open_null_file) 12 | TEST_DECLARE(protobuf2json_file__error_cannot_open_null_fopen_mode) 13 | TEST_DECLARE(protobuf2json_file__error_cannot_open_unexistent_file) 14 | TEST_DECLARE(protobuf2json_file__error_cannot_dump_file) 15 | 16 | TEST_DECLARE(protobuf2json_string__required_field) 17 | TEST_DECLARE(protobuf2json_string__optional_field) 18 | TEST_DECLARE(protobuf2json_string__default_values) 19 | TEST_DECLARE(protobuf2json_string__oneof) 20 | TEST_DECLARE(protobuf2json_string__error_in_nested_message) 21 | TEST_DECLARE(protobuf2json_string__error_cannot_create_json_object) 22 | TEST_DECLARE(protobuf2json_string__error_cannot_create_json_value) 23 | TEST_DECLARE(protobuf2json_string__error_in_json_object_set_new_1) 24 | TEST_DECLARE(protobuf2json_string__error_in_json_object_set_new_2) 25 | TEST_DECLARE(protobuf2json_string__error_cannot_create_json_array) 26 | TEST_DECLARE(protobuf2json_string__error_in_json_object_set_new_3) 27 | TEST_DECLARE(protobuf2json_string__error_cannot_dump_string) 28 | 29 | TEST_DECLARE(json2protobuf_file__success) 30 | TEST_DECLARE(json2protobuf_file__error_cannot_parse_bad_message) 31 | TEST_DECLARE(json2protobuf_file__error_cannot_parse_bad_json) 32 | TEST_DECLARE(json2protobuf_file__error_cannot_parse_unexistent_file) 33 | 34 | TEST_DECLARE(json2protobuf_string__error_cannot_parse_wrong_string) 35 | TEST_DECLARE(json2protobuf_string__error_duplicate_field) 36 | TEST_DECLARE(json2protobuf_string__error_is_not_array) 37 | TEST_DECLARE(json2protobuf_string__error_unknown_field) 38 | TEST_DECLARE(json2protobuf_string__error_unknown_enum_value) 39 | TEST_DECLARE(json2protobuf_string__error_required_is_missing) 40 | TEST_DECLARE(json2protobuf_string__error_is_not_object_required_for_message) 41 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_int32) 42 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_sint32) 43 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_sfixed32) 44 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_uint32) 45 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_fixed32) 46 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_int64) 47 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_sint64) 48 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_sfixed64) 49 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_uint64) 50 | TEST_DECLARE(json2protobuf_string__error_is_not_integer_required_for_fixed64) 51 | TEST_DECLARE(json2protobuf_string__error_is_not_real_number_required_for_float) 52 | TEST_DECLARE(json2protobuf_string__error_is_not_real_number_required_for_double) 53 | TEST_DECLARE(json2protobuf_string__error_is_not_boolean_required_for_bool) 54 | TEST_DECLARE(json2protobuf_string__error_is_not_string_required_for_enum) 55 | TEST_DECLARE(json2protobuf_string__error_is_not_string_required_for_string) 56 | TEST_DECLARE(json2protobuf_string__error_is_not_string_required_for_bytes) 57 | 58 | TEST_DECLARE(reversible__messages) 59 | TEST_DECLARE(reversible__default_values) 60 | TEST_DECLARE(reversible__numbers) 61 | TEST_DECLARE(reversible__strings) 62 | TEST_DECLARE(reversible__bytes) 63 | TEST_DECLARE(reversible__oneof_none) 64 | TEST_DECLARE(reversible__oneof_one) 65 | TEST_DECLARE(reversible__oneof_other) 66 | TEST_DECLARE(reversible__oneof_both_first) 67 | TEST_DECLARE(reversible__oneof_both_second) 68 | 69 | TASK_LIST_START 70 | TEST_ENTRY(protobuf2json_file__success) 71 | TEST_ENTRY(protobuf2json_file__error_alloc) 72 | TEST_ENTRY(protobuf2json_file__error_cannot_open_null_file) 73 | TEST_ENTRY(protobuf2json_file__error_cannot_open_null_fopen_mode) 74 | TEST_ENTRY(protobuf2json_file__error_cannot_open_unexistent_file) 75 | TEST_ENTRY(protobuf2json_file__error_cannot_dump_file) 76 | 77 | TEST_ENTRY(protobuf2json_string__required_field) 78 | TEST_ENTRY(protobuf2json_string__optional_field) 79 | TEST_ENTRY(protobuf2json_string__default_values) 80 | TEST_ENTRY(protobuf2json_string__oneof) 81 | TEST_ENTRY(protobuf2json_string__error_in_nested_message) 82 | TEST_ENTRY(protobuf2json_string__error_cannot_create_json_object) 83 | TEST_ENTRY(protobuf2json_string__error_cannot_create_json_value) 84 | TEST_ENTRY(protobuf2json_string__error_in_json_object_set_new_1) 85 | TEST_ENTRY(protobuf2json_string__error_in_json_object_set_new_2) 86 | TEST_ENTRY(protobuf2json_string__error_cannot_create_json_array) 87 | TEST_ENTRY(protobuf2json_string__error_in_json_object_set_new_3) 88 | TEST_ENTRY(protobuf2json_string__error_cannot_dump_string) 89 | 90 | TEST_ENTRY(json2protobuf_file__success) 91 | TEST_ENTRY(json2protobuf_file__error_cannot_parse_bad_message) 92 | TEST_ENTRY(json2protobuf_file__error_cannot_parse_bad_json) 93 | TEST_ENTRY(json2protobuf_file__error_cannot_parse_unexistent_file) 94 | 95 | TEST_ENTRY(json2protobuf_string__error_cannot_parse_wrong_string) 96 | TEST_ENTRY(json2protobuf_string__error_duplicate_field) 97 | TEST_ENTRY(json2protobuf_string__error_is_not_array) 98 | TEST_ENTRY(json2protobuf_string__error_unknown_field) 99 | TEST_ENTRY(json2protobuf_string__error_unknown_enum_value) 100 | TEST_ENTRY(json2protobuf_string__error_required_is_missing) 101 | TEST_ENTRY(json2protobuf_string__error_is_not_object_required_for_message) 102 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_int32) 103 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_sint32) 104 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_sfixed32) 105 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_uint32) 106 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_fixed32) 107 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_int64) 108 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_sint64) 109 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_sfixed64) 110 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_uint64) 111 | TEST_ENTRY(json2protobuf_string__error_is_not_integer_required_for_fixed64) 112 | TEST_ENTRY(json2protobuf_string__error_is_not_real_number_required_for_float) 113 | TEST_ENTRY(json2protobuf_string__error_is_not_real_number_required_for_double) 114 | TEST_ENTRY(json2protobuf_string__error_is_not_boolean_required_for_bool) 115 | TEST_ENTRY(json2protobuf_string__error_is_not_string_required_for_enum) 116 | TEST_ENTRY(json2protobuf_string__error_is_not_string_required_for_string) 117 | TEST_ENTRY(json2protobuf_string__error_is_not_string_required_for_bytes) 118 | 119 | TEST_ENTRY(reversible__messages) 120 | TEST_ENTRY(reversible__default_values) 121 | TEST_ENTRY(reversible__numbers) 122 | TEST_ENTRY(reversible__strings) 123 | TEST_ENTRY(reversible__bytes) 124 | TEST_ENTRY(reversible__oneof_none) 125 | TEST_ENTRY(reversible__oneof_one) 126 | TEST_ENTRY(reversible__oneof_other) 127 | TEST_ENTRY(reversible__oneof_both_first) 128 | TEST_ENTRY(reversible__oneof_both_second) 129 | TASK_LIST_END 130 | -------------------------------------------------------------------------------- /test/test-protobuf2json-file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #include "task.h" 10 | #include "test.pb-c.h" 11 | #include "protobuf2json.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | extern char executable_path[MAXPATHLEN]; 18 | 19 | TEST_IMPL(protobuf2json_file__success) { 20 | int result; 21 | 22 | char file_path[MAXPATHLEN] = {0}; 23 | char file_name[MAXPATHLEN] = {0}; 24 | long json_string_length = 0; 25 | 26 | result = realpath(dirname(executable_path), file_path) ? 1 : 0; 27 | ASSERT(result > 0); 28 | 29 | result = snprintf(file_name, sizeof(file_name) - 1, "%s/existent.json", file_path); 30 | ASSERT(result > 0); 31 | 32 | Foo__Person person = FOO__PERSON__INIT; 33 | 34 | person.name = "John Doe"; 35 | person.id = 42; 36 | 37 | result = protobuf2json_file(&person.base, TEST_JSON_FLAGS, file_name, "w", NULL, 0); 38 | ASSERT_ZERO(result); 39 | 40 | FILE *fd = fopen(file_name, "r"); 41 | ASSERT(fd); 42 | 43 | result = fseek(fd, 0, SEEK_END); 44 | ASSERT_ZERO(result); 45 | json_string_length = ftell(fd); 46 | ASSERT(json_string_length > 0); 47 | rewind(fd); 48 | 49 | char *json_string = (char *)calloc(sizeof(char), json_string_length + 1); 50 | ASSERT(json_string); 51 | 52 | result = fread(json_string, 1, json_string_length, fd); 53 | ASSERT(result == json_string_length); 54 | json_string[json_string_length] = '\0'; 55 | 56 | ASSERT_STRCMP( 57 | json_string, 58 | "{\n" 59 | " \"name\": \"John Doe\",\n" 60 | " \"id\": 42\n" 61 | "}" 62 | ); 63 | 64 | free(json_string); 65 | fclose(fd); 66 | 67 | result = unlink(file_name); 68 | ASSERT_ZERO(result); 69 | 70 | RETURN_OK(); 71 | } 72 | 73 | void* failed_malloc(size_t size) { 74 | return NULL; 75 | } 76 | 77 | TEST_IMPL(protobuf2json_file__error_alloc) { 78 | int result; 79 | char error_string[256] = {0}; 80 | 81 | Foo__Person person = FOO__PERSON__INIT; 82 | 83 | person.name = "John Doe"; 84 | person.id = 42; 85 | 86 | json_set_alloc_funcs(failed_malloc, free); 87 | result = protobuf2json_file(&person.base, 0, "not null", "r", error_string, sizeof(error_string)); 88 | json_set_alloc_funcs(malloc, free); 89 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY); 90 | 91 | const char *expected_error_string = \ 92 | "Cannot allocate JSON structure using json_object()" 93 | ; 94 | 95 | ASSERT_STRCMP( 96 | error_string, 97 | expected_error_string 98 | ); 99 | 100 | RETURN_OK(); 101 | } 102 | 103 | TEST_IMPL(protobuf2json_file__error_cannot_open_null_file) { 104 | int result; 105 | char error_string[256] = {0}; 106 | 107 | Foo__Person person = FOO__PERSON__INIT; 108 | 109 | person.name = "John Doe"; 110 | person.id = 42; 111 | 112 | result = protobuf2json_file(&person.base, 0, NULL, "r", error_string, sizeof(error_string)); 113 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_DUMP_FILE); 114 | 115 | const char *expected_error_string = \ 116 | "Cannot open NULL to dump JSON" 117 | ; 118 | 119 | ASSERT_STRCMP( 120 | error_string, 121 | expected_error_string 122 | ); 123 | 124 | RETURN_OK(); 125 | } 126 | 127 | TEST_IMPL(protobuf2json_file__error_cannot_open_null_fopen_mode) { 128 | int result; 129 | char error_string[256] = {0}; 130 | 131 | Foo__Person person = FOO__PERSON__INIT; 132 | 133 | person.name = "John Doe"; 134 | person.id = 42; 135 | 136 | result = protobuf2json_file(&person.base, 0, "not null", NULL, error_string, sizeof(error_string)); 137 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_DUMP_FILE); 138 | 139 | const char *expected_error_string = \ 140 | "Cannot open file with NULL fopen(3) mode to dump JSON" 141 | ; 142 | 143 | ASSERT_STRCMP( 144 | error_string, 145 | expected_error_string 146 | ); 147 | 148 | RETURN_OK(); 149 | } 150 | 151 | TEST_IMPL(protobuf2json_file__error_cannot_open_unexistent_file) { 152 | int result; 153 | char error_string[256] = {0}; 154 | 155 | char file_path[MAXPATHLEN] = {0}; 156 | char file_name[MAXPATHLEN] = {0}; 157 | long json_string_length = 0; 158 | 159 | result = realpath(dirname(executable_path), file_path) ? 1 : 0; 160 | ASSERT(result > 0); 161 | 162 | result = snprintf(file_name, sizeof(file_name) - 1, "%s/fixtures/unexistent.json", file_path); 163 | ASSERT(result > 0); 164 | 165 | Foo__Person person = FOO__PERSON__INIT; 166 | 167 | person.name = "John Doe"; 168 | person.id = 42; 169 | 170 | result = protobuf2json_file(&person.base, 0, file_name, "r", error_string, sizeof(error_string)); 171 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_DUMP_FILE); 172 | 173 | char expected_error_string[MAXPATHLEN + 100]; 174 | sprintf(expected_error_string, "Cannot open file '%s' with mode 'r' to dump JSON, errno=%d", file_name, ENOENT); 175 | 176 | ASSERT_STRCMP( 177 | error_string, 178 | (const char *)expected_error_string 179 | ); 180 | 181 | RETURN_OK(); 182 | } 183 | 184 | TEST_IMPL(protobuf2json_file__error_cannot_dump_file) { 185 | int result; 186 | char error_string[256] = {0}; 187 | 188 | char file_path[MAXPATHLEN] = {0}; 189 | char file_name[MAXPATHLEN] = {0}; 190 | long json_string_length = 0; 191 | 192 | result = realpath(dirname(executable_path), file_path) ? 1 : 0; 193 | ASSERT(result > 0); 194 | 195 | result = snprintf(file_name, sizeof(file_name) - 1, "%s/fixtures/good.json", file_path); 196 | ASSERT(result > 0); 197 | 198 | Foo__Person person = FOO__PERSON__INIT; 199 | 200 | person.name = "John Doe"; 201 | person.id = 42; 202 | 203 | result = protobuf2json_file(&person.base, 0, file_name, "r", error_string, sizeof(error_string)); 204 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_DUMP_FILE); 205 | 206 | char expected_error_string[MAXPATHLEN + 100]; 207 | sprintf(expected_error_string, "Cannot write JSON to file '%s' with mode 'r', errno=%d", file_name, EBADF); 208 | 209 | ASSERT_STRCMP( 210 | error_string, 211 | (const char *)expected_error_string 212 | ); 213 | 214 | RETURN_OK(); 215 | } 216 | -------------------------------------------------------------------------------- /test/test-protobuf2json-string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #include "task.h" 10 | #include "test.pb-c.h" 11 | #include "protobuf2json.h" 12 | 13 | #include "failed-alloc-helper.h" 14 | 15 | TEST_IMPL(protobuf2json_string__required_field) { 16 | int result; 17 | 18 | Foo__Person person = FOO__PERSON__INIT; 19 | 20 | person.name = "John Doe"; 21 | person.id = 42; 22 | 23 | char *json_string; 24 | result = protobuf2json_string(&person.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 25 | ASSERT_ZERO(result); 26 | ASSERT(json_string); 27 | 28 | ASSERT_STRCMP( 29 | json_string, 30 | "{\n" 31 | " \"name\": \"John Doe\",\n" 32 | " \"id\": 42\n" 33 | "}" 34 | ); 35 | 36 | free(json_string); 37 | 38 | RETURN_OK(); 39 | } 40 | 41 | TEST_IMPL(protobuf2json_string__optional_field) { 42 | int result; 43 | 44 | Foo__Person person = FOO__PERSON__INIT; 45 | 46 | person.name = "John Doe"; 47 | person.id = 42; 48 | 49 | person.email = "john@doe.name"; 50 | 51 | char *json_string; 52 | result = protobuf2json_string(&person.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 53 | ASSERT_ZERO(result); 54 | ASSERT(json_string); 55 | 56 | ASSERT_STRCMP( 57 | json_string, 58 | "{\n" 59 | " \"name\": \"John Doe\",\n" 60 | " \"id\": 42,\n" 61 | " \"email\": \"john@doe.name\"\n" 62 | "}" 63 | ); 64 | 65 | free(json_string); 66 | 67 | RETURN_OK(); 68 | } 69 | 70 | TEST_IMPL(protobuf2json_string__default_values) { 71 | int result; 72 | 73 | Foo__Bar bar = FOO__BAR__INIT; 74 | 75 | bar.string_required = "required"; 76 | 77 | char *json_string; 78 | result = protobuf2json_string(&bar.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 79 | ASSERT_ZERO(result); 80 | ASSERT(json_string); 81 | 82 | ASSERT_STRCMP( 83 | json_string, 84 | "{\n" 85 | " \"string_required\": \"required\",\n" 86 | " \"string_required_default\": \"default value 1\",\n" 87 | " \"string_optional_default\": \"default value 2\",\n" 88 | " \"bytes_optional_default\": \"ZGVmYXVsdCB2YWx1ZSAz\",\n" 89 | " \"enum_optional_default\": \"FIZZBUZZ\"\n" 90 | "}" 91 | ); 92 | 93 | free(json_string); 94 | 95 | RETURN_OK(); 96 | } 97 | 98 | TEST_IMPL(protobuf2json_string__oneof) { 99 | int result; 100 | 101 | Foo__Something something = FOO__SOMETHING__INIT; 102 | 103 | char *json_string; 104 | 105 | // FOO__SOMETHING__SOMETHING_ONEOF_STRING but not set 106 | something.something_case = FOO__SOMETHING__SOMETHING_ONEOF_STRING; 107 | something.oneof_string = NULL; 108 | result = protobuf2json_string(&something.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 109 | ASSERT_ZERO(result); 110 | ASSERT(json_string); 111 | 112 | ASSERT_STRCMP( 113 | json_string, 114 | "{}" 115 | ); 116 | 117 | free(json_string); 118 | 119 | // FOO__SOMETHING__SOMETHING__NOT_SET 120 | something.something_case = FOO__SOMETHING__SOMETHING__NOT_SET; 121 | result = protobuf2json_string(&something.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 122 | ASSERT_ZERO(result); 123 | ASSERT(json_string); 124 | 125 | ASSERT_STRCMP( 126 | json_string, 127 | "{}" 128 | ); 129 | 130 | free(json_string); 131 | 132 | // FOO__SOMETHING__SOMETHING_ONEOF_STRING 133 | something.something_case = FOO__SOMETHING__SOMETHING_ONEOF_STRING; 134 | something.oneof_string = "string"; 135 | result = protobuf2json_string(&something.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 136 | ASSERT_ZERO(result); 137 | ASSERT(json_string); 138 | 139 | ASSERT_STRCMP( 140 | json_string, 141 | "{\n" 142 | " \"oneof_string\": \"string\"\n" 143 | "}" 144 | ); 145 | 146 | free(json_string); 147 | 148 | // FOO__SOMETHING__SOMETHING_ONEOF_BYTES 149 | something.oneof_bytes.len = 5; 150 | something.oneof_bytes.data = (uint8_t*)"bytes"; 151 | something.something_case = FOO__SOMETHING__SOMETHING_ONEOF_BYTES; 152 | result = protobuf2json_string(&something.base, TEST_JSON_FLAGS, &json_string, NULL, 0); 153 | ASSERT_ZERO(result); 154 | ASSERT(json_string); 155 | 156 | ASSERT_STRCMP( 157 | json_string, 158 | "{\n" 159 | " \"oneof_bytes\": \"Ynl0ZXM=\"\n" 160 | "}" 161 | ); 162 | 163 | free(json_string); 164 | 165 | RETURN_OK(); 166 | } 167 | 168 | TEST_IMPL(protobuf2json_string__error_in_nested_message) { 169 | int result; 170 | char error_string[256] = {0}; 171 | 172 | Foo__Person person = FOO__PERSON__INIT; 173 | 174 | person.name = "John Doe"; 175 | person.id = 42; 176 | 177 | Foo__Person__PhoneNumber person_phonenumber1 = FOO__PERSON__PHONE_NUMBER__INIT; 178 | 179 | person_phonenumber1.number = "+123456789"; 180 | person_phonenumber1.has_type = 1; 181 | person_phonenumber1.type = 777; // Unknown enum value 182 | 183 | person.n_phone = 1; 184 | person.phone = calloc(person.n_phone, sizeof(Foo__Person__PhoneNumber*)); 185 | ASSERT(person.phone); 186 | 187 | person.phone[0] = (Foo__Person__PhoneNumber*)&person_phonenumber1; 188 | 189 | char *json_string; 190 | result = protobuf2json_string(&person.base, 0, &json_string, error_string, sizeof(error_string)); 191 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_UNKNOWN_ENUM_VALUE); 192 | 193 | const char *expected_error_string = \ 194 | "Unknown value 777 for enum 'Foo.Person.PhoneType'" 195 | ; 196 | 197 | ASSERT_STRCMP( 198 | error_string, 199 | expected_error_string 200 | ); 201 | 202 | free(person.phone); 203 | 204 | RETURN_OK(); 205 | } 206 | 207 | TEST_IMPL(protobuf2json_string__error_cannot_create_json_object) { 208 | int result; 209 | char error_string[256] = {0}; 210 | 211 | Foo__Person person = FOO__PERSON__INIT; 212 | 213 | person.name = "John Doe"; 214 | person.id = 42; 215 | 216 | char *json_string; 217 | failed_alloc_json_set_by_count(1); 218 | result = protobuf2json_string(&person.base, 0, &json_string, error_string, sizeof(error_string)); 219 | failed_alloc_json_unset(); 220 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY); 221 | 222 | const char *expected_error_string = \ 223 | "Cannot allocate JSON structure using json_object()" 224 | ; 225 | 226 | ASSERT_STRCMP( 227 | error_string, 228 | expected_error_string 229 | ); 230 | 231 | RETURN_OK(); 232 | } 233 | 234 | TEST_IMPL(protobuf2json_string__error_cannot_create_json_value) { 235 | int result; 236 | char error_string[256] = {0}; 237 | 238 | Foo__Person person = FOO__PERSON__INIT; 239 | 240 | person.name = "John Doe"; 241 | person.id = 42; 242 | 243 | char *json_string; 244 | failed_alloc_json_set_by_count(2); 245 | result = protobuf2json_string(&person.base, 0, &json_string, error_string, sizeof(error_string)); 246 | failed_alloc_json_unset(); 247 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY); 248 | 249 | const char *expected_error_string = \ 250 | "Cannot allocate JSON structure in protobuf2json_process_field()" 251 | ; 252 | 253 | ASSERT_STRCMP( 254 | error_string, 255 | expected_error_string 256 | ); 257 | 258 | RETURN_OK(); 259 | } 260 | 261 | TEST_IMPL(protobuf2json_string__error_in_json_object_set_new_1) { 262 | int result; 263 | char error_string[256] = {0}; 264 | 265 | Foo__Person person = FOO__PERSON__INIT; 266 | 267 | person.name = "John Doe"; 268 | person.id = 42; 269 | 270 | person.email = "john@doe.name"; 271 | 272 | char *json_string; 273 | failed_alloc_json_set_by_count(4); 274 | result = protobuf2json_string(&person.base, 0, &json_string, error_string, sizeof(error_string)); 275 | failed_alloc_json_unset(); 276 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_JANSSON_INTERNAL); 277 | 278 | const char *expected_error_string = \ 279 | "Error in json_object_set_new()" 280 | ; 281 | 282 | ASSERT_STRCMP( 283 | error_string, 284 | expected_error_string 285 | ); 286 | 287 | RETURN_OK(); 288 | } 289 | 290 | TEST_IMPL(protobuf2json_string__error_in_json_object_set_new_2) { 291 | int result; 292 | char error_string[256] = {0}; 293 | 294 | Foo__Person person = FOO__PERSON__INIT; 295 | 296 | person.name = "John Doe"; 297 | person.id = 42; 298 | 299 | person.email = "john@doe.name"; 300 | 301 | char *json_string; 302 | failed_alloc_json_set_by_count(9); 303 | result = protobuf2json_string(&person.base, 0, &json_string, error_string, sizeof(error_string)); 304 | failed_alloc_json_unset(); 305 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_JANSSON_INTERNAL); 306 | 307 | const char *expected_error_string = \ 308 | "Error in json_object_set_new()" 309 | ; 310 | 311 | ASSERT_STRCMP( 312 | error_string, 313 | expected_error_string 314 | ); 315 | 316 | RETURN_OK(); 317 | } 318 | 319 | TEST_IMPL(protobuf2json_string__error_cannot_create_json_array) { 320 | int result; 321 | char error_string[256] = {0}; 322 | 323 | Foo__Person person = FOO__PERSON__INIT; 324 | 325 | person.name = "John Doe"; 326 | person.id = 42; 327 | 328 | person.email = "john@doe.name"; 329 | 330 | person.n_phone = 1; 331 | 332 | char *json_string; 333 | failed_alloc_json_set_by_count(10); 334 | result = protobuf2json_string(&person.base, 0, &json_string, error_string, sizeof(error_string)); 335 | failed_alloc_json_unset(); 336 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_ALLOCATE_MEMORY); 337 | 338 | const char *expected_error_string = \ 339 | "Cannot allocate JSON structure using json_array()" 340 | ; 341 | 342 | ASSERT_STRCMP( 343 | error_string, 344 | expected_error_string 345 | ); 346 | 347 | RETURN_OK(); 348 | } 349 | 350 | TEST_IMPL(protobuf2json_string__error_in_json_object_set_new_3) { 351 | int result; 352 | char error_string[256] = {0}; 353 | 354 | Foo__Person person = FOO__PERSON__INIT; 355 | 356 | person.name = "John Doe"; 357 | person.id = 42; 358 | 359 | person.email = "john@doe.name"; 360 | 361 | Foo__Person__PhoneNumber person_phonenumber1 = FOO__PERSON__PHONE_NUMBER__INIT; 362 | 363 | person_phonenumber1.number = "+123456789"; 364 | 365 | person.n_phone = 1; 366 | person.phone = calloc(person.n_phone, sizeof(Foo__Person__PhoneNumber*)); 367 | ASSERT(person.phone); 368 | 369 | person.phone[0] = (Foo__Person__PhoneNumber*)&person_phonenumber1; 370 | 371 | char *json_string; 372 | failed_alloc_json_set_by_count(20); 373 | result = protobuf2json_string(&person.base, 0, &json_string, error_string, sizeof(error_string)); 374 | failed_alloc_json_unset(); 375 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_JANSSON_INTERNAL); 376 | 377 | const char *expected_error_string = \ 378 | "Error in json_object_set_new()" 379 | ; 380 | 381 | ASSERT_STRCMP( 382 | error_string, 383 | expected_error_string 384 | ); 385 | 386 | free(person.phone); 387 | 388 | RETURN_OK(); 389 | } 390 | 391 | TEST_IMPL(protobuf2json_string__error_cannot_dump_string) { 392 | int result; 393 | char error_string[256] = {0}; 394 | 395 | Foo__Person person = FOO__PERSON__INIT; 396 | 397 | person.name = "John Doe"; 398 | person.id = 42; 399 | 400 | char *json_string; 401 | failed_alloc_json_set_by_size(16 /* STRBUFFER_MIN_SIZE */); 402 | result = protobuf2json_string(&person.base, 0, &json_string, error_string, sizeof(error_string)); 403 | failed_alloc_json_unset(); 404 | ASSERT_EQUALS(result, PROTOBUF2JSON_ERR_CANNOT_DUMP_STRING); 405 | 406 | const char *expected_error_string = \ 407 | "Cannot dump JSON object to string using json_dumps()" 408 | ; 409 | 410 | ASSERT_STRCMP( 411 | error_string, 412 | expected_error_string 413 | ); 414 | 415 | RETURN_OK(); 416 | } 417 | -------------------------------------------------------------------------------- /test/test-reversible.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 Oleg Efimov 3 | * 4 | * protobuf2json-c is free software; you can redistribute it 5 | * and/or modify it under the terms of the MIT license. 6 | * See LICENSE for details. 7 | */ 8 | 9 | #include "task.h" 10 | #include "test.pb-c.h" 11 | #include "protobuf2json.h" 12 | 13 | #include 14 | 15 | TEST_IMPL(reversible__messages) { 16 | int result; 17 | 18 | const char *initial_json_string = \ 19 | "{\n" 20 | " \"name\": \"John Doe\",\n" 21 | " \"id\": 42,\n" 22 | " \"email\": \"john@doe.name\",\n" 23 | " \"phone\": [\n" 24 | " {\n" 25 | " \"number\": \"+123456789\",\n" 26 | " \"type\": \"WORK\"\n" 27 | " },\n" 28 | " {\n" 29 | " \"number\": \"+987654321\",\n" 30 | " \"type\": \"MOBILE\"\n" 31 | " },\n" 32 | " {\n" 33 | " \"number\": \"+555555555\"\n" 34 | " }\n" 35 | " ]\n" 36 | "}" 37 | ; 38 | 39 | ProtobufCMessage *protobuf_message; 40 | 41 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__person__descriptor, &protobuf_message, NULL, 0); 42 | ASSERT_ZERO(result); 43 | 44 | Foo__Person *person = (Foo__Person *)protobuf_message; 45 | 46 | ASSERT(person->id == 42); 47 | ASSERT(person->name); 48 | ASSERT_STRCMP(person->name, "John Doe"); 49 | ASSERT(person->email); 50 | ASSERT_STRCMP(person->email, "john@doe.name"); 51 | 52 | ASSERT(person->n_phone == 3); 53 | 54 | ASSERT(person->phone[1]->number); 55 | ASSERT_STRCMP(person->phone[1]->number, "+987654321"); 56 | ASSERT(person->phone[1]->type == FOO__PERSON__PHONE_TYPE__MOBILE); 57 | 58 | char *json_string; 59 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 60 | ASSERT_ZERO(result); 61 | ASSERT(json_string); 62 | 63 | const char *expected_json_string = \ 64 | "{\n" 65 | " \"name\": \"John Doe\",\n" 66 | " \"id\": 42,\n" 67 | " \"email\": \"john@doe.name\",\n" 68 | " \"phone\": [\n" 69 | " {\n" 70 | " \"number\": \"+123456789\",\n" 71 | " \"type\": \"WORK\"\n" 72 | " },\n" 73 | " {\n" 74 | " \"number\": \"+987654321\",\n" 75 | " \"type\": \"MOBILE\"\n" 76 | " },\n" 77 | " {\n" 78 | " \"number\": \"+555555555\",\n" 79 | " \"type\": \"HOME\"\n" 80 | " }\n" 81 | " ]\n" 82 | "}" 83 | ; 84 | 85 | ASSERT_STRCMP( 86 | json_string, 87 | expected_json_string 88 | ); 89 | 90 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 91 | free(json_string); 92 | 93 | RETURN_OK(); 94 | } 95 | 96 | TEST_IMPL(reversible__default_values) { 97 | int result; 98 | 99 | const char *initial_json_string = \ 100 | "{\n" 101 | " \"string_required\": \"required\"\n" 102 | "}" 103 | ; 104 | 105 | ProtobufCMessage *protobuf_message; 106 | 107 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__bar__descriptor, &protobuf_message, NULL, 0); 108 | ASSERT_ZERO(result); 109 | 110 | Foo__Bar *bar = (Foo__Bar *)protobuf_message; 111 | ASSERT_STRCMP(bar->string_required, "required"); 112 | 113 | char *json_string; 114 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 115 | ASSERT_ZERO(result); 116 | ASSERT(json_string); 117 | 118 | const char *expected_json_string = \ 119 | "{\n" 120 | " \"string_required\": \"required\",\n" 121 | " \"string_required_default\": \"default value 1\",\n" 122 | " \"string_optional_default\": \"default value 2\",\n" 123 | " \"bytes_optional_default\": \"ZGVmYXVsdCB2YWx1ZSAz\",\n" 124 | " \"enum_optional_default\": \"FIZZBUZZ\"\n" 125 | "}" 126 | ; 127 | 128 | ASSERT_STRCMP( 129 | json_string, 130 | expected_json_string 131 | ); 132 | 133 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 134 | free(json_string); 135 | 136 | RETURN_OK(); 137 | } 138 | 139 | TEST_IMPL(reversible__numbers) { 140 | int result; 141 | 142 | const char *initial_json_string = \ 143 | "{\n" 144 | 145 | " \"value_int32\": [2147483647, 0],\n" 146 | " \"value_sint32\": [-2147483648, 0],\n" 147 | " \"value_sfixed32\": [-2147483648, 0],\n" 148 | 149 | " \"value_uint32\": [4294967295, 0],\n"\ 150 | " \"value_fixed32\": [4294967295, 0],\n" 151 | 152 | " \"value_int64\": [9223372036854775807, 0],\n" 153 | " \"value_sint64\": [-9223372036854775808, 0],\n" 154 | " \"value_sfixed64\": [-9223372036854775808, 0],\n" 155 | 156 | /* JSON does not support max(unsigned long long) */ 157 | " \"value_uint64\": [9223372036854775807, 0],\n" 158 | " \"value_fixed64\": [9223372036854775807, 0],\n" 159 | 160 | " \"value_float\": [0.33000001311302185, 0],\n" 161 | " \"value_double\": [0.0077705550333011103, 0],\n" 162 | 163 | " \"value_bool\": [true, false, false]\n" 164 | 165 | "}" 166 | ; 167 | 168 | ProtobufCMessage *protobuf_message; 169 | 170 | result = json2protobuf_string((char *)initial_json_string, 0, &foo__repeated_values__descriptor, &protobuf_message, NULL, 0); 171 | ASSERT_ZERO(result); 172 | 173 | Foo__RepeatedValues *repeated_values = (Foo__RepeatedValues *)protobuf_message; 174 | 175 | ASSERT(repeated_values->n_value_int32 == 2); 176 | ASSERT(repeated_values->value_int32[0] == 2147483647); 177 | ASSERT(repeated_values->value_int32[1] == 0); 178 | 179 | ASSERT(repeated_values->n_value_sint32 == 2); 180 | ASSERT(repeated_values->value_sint32[0] == -2147483647 - 1); 181 | ASSERT(repeated_values->value_sint32[1] == 0); 182 | 183 | ASSERT(repeated_values->n_value_sfixed32 == 2); 184 | ASSERT(repeated_values->value_sfixed32[0] == -2147483647 - 1); 185 | ASSERT(repeated_values->value_sfixed32[1] == 0); 186 | 187 | ASSERT(repeated_values->n_value_uint32 == 2); 188 | ASSERT(repeated_values->value_uint32[0] == 4294967295); 189 | ASSERT(repeated_values->value_uint32[1] == 0); 190 | 191 | ASSERT(repeated_values->n_value_fixed32 == 2); 192 | ASSERT(repeated_values->value_fixed32[0] == 4294967295); 193 | ASSERT(repeated_values->value_fixed32[1] == 0); 194 | 195 | ASSERT(repeated_values->n_value_int64 == 2); 196 | ASSERT(repeated_values->value_int64[0] == 9223372036854775807); 197 | ASSERT(repeated_values->value_int64[1] == 0); 198 | 199 | ASSERT(repeated_values->n_value_sint64 == 2); 200 | ASSERT(repeated_values->value_sint64[0] == -9223372036854775807 - 1); 201 | ASSERT(repeated_values->value_sint64[1] == 0); 202 | 203 | ASSERT(repeated_values->n_value_sfixed64 == 2); 204 | ASSERT(repeated_values->value_sfixed64[0] == -9223372036854775807 - 1); 205 | ASSERT(repeated_values->value_sfixed64[1] == 0); 206 | 207 | /* JSON does not support max(unsigned long long) */ 208 | ASSERT(repeated_values->n_value_uint64 == 2); 209 | ASSERT(repeated_values->value_uint64[0] == 9223372036854775807); 210 | ASSERT(repeated_values->value_uint64[1] == 0); 211 | 212 | ASSERT(repeated_values->n_value_fixed64 == 2); 213 | ASSERT(repeated_values->value_fixed64[0] == 9223372036854775807); 214 | ASSERT(repeated_values->value_fixed64[1] == 0); 215 | 216 | ASSERT(repeated_values->n_value_float == 2); 217 | ASSERT(fabs(repeated_values->value_float[0] - 0.33000001311302185) < 1e-10); 218 | ASSERT(fabs(repeated_values->value_float[1]) < 1e-10); 219 | 220 | ASSERT(repeated_values->n_value_double == 2); 221 | ASSERT(fabs(repeated_values->value_double[0] - 0.0077705550333011103) < 1e-10); 222 | ASSERT(fabs(repeated_values->value_double[1]) < 1e-10); 223 | 224 | ASSERT(repeated_values->n_value_bool == 3); 225 | ASSERT(repeated_values->value_bool[0]); 226 | ASSERT(!repeated_values->value_bool[1]); 227 | ASSERT(!repeated_values->value_bool[2]); 228 | 229 | char *json_string; 230 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 231 | ASSERT_ZERO(result); 232 | ASSERT(json_string); 233 | 234 | const char *expected_json_string = \ 235 | "{\n" 236 | 237 | " \"value_int32\": [\n" 238 | " 2147483647,\n" 239 | " 0\n" 240 | " ],\n" 241 | " \"value_sint32\": [\n" 242 | " -2147483648,\n" 243 | " 0\n" 244 | " ],\n" 245 | " \"value_sfixed32\": [\n" 246 | " -2147483648,\n" 247 | " 0\n" 248 | " ],\n" 249 | 250 | " \"value_uint32\": [\n" 251 | " 4294967295,\n" 252 | " 0\n" 253 | " ],\n"\ 254 | " \"value_fixed32\": [\n" 255 | " 4294967295,\n" 256 | " 0\n" 257 | " ],\n" 258 | 259 | " \"value_int64\": [\n" 260 | " 9223372036854775807,\n" 261 | " 0\n" 262 | " ],\n" 263 | " \"value_sint64\": [\n" 264 | " -9223372036854775808,\n" 265 | " 0\n" 266 | " ],\n" 267 | " \"value_sfixed64\": [\n" 268 | " -9223372036854775808,\n" 269 | " 0\n" 270 | " ],\n" 271 | 272 | /* JSON does not support max(unsigned long long) */ 273 | " \"value_uint64\": [\n" 274 | " 9223372036854775807,\n" 275 | " 0\n" 276 | " ],\n" 277 | " \"value_fixed64\": [\n" 278 | " 9223372036854775807,\n" 279 | " 0\n" 280 | " ],\n" 281 | 282 | " \"value_float\": [\n" 283 | " 0.33000001311302185,\n" 284 | " 0.0\n" /* Note: 0 -> 0.0 */ 285 | " ],\n" 286 | " \"value_double\": [\n" 287 | " 0.0077705550333011103,\n" 288 | " 0.0\n" /* Note: 0 -> 0.0 */ 289 | " ],\n" 290 | 291 | " \"value_bool\": [\n" 292 | " true,\n" 293 | " false,\n" 294 | " false\n" 295 | " ]\n" 296 | 297 | "}" 298 | ; 299 | 300 | ASSERT_STRCMP( 301 | json_string, 302 | expected_json_string 303 | ); 304 | 305 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 306 | free(json_string); 307 | 308 | RETURN_OK(); 309 | } 310 | 311 | TEST_IMPL(reversible__strings) { 312 | int result; 313 | 314 | const char *initial_json_string = \ 315 | "{\n" 316 | " \"value_string\": [\n" 317 | " \"qwerty \\u0000 12345\",\n" /* Note: \0-byte terminated string */ 318 | " \"qwerty \\u0000\",\n" /* Note: \0-byte terminated string */ 319 | " \"\\u0000 12345\"\n" /* Note: \0-byte terminated string */ 320 | " ]\n" 321 | "}" 322 | ; 323 | 324 | ProtobufCMessage *protobuf_message; 325 | 326 | result = json2protobuf_string((char *)initial_json_string, JSON_ALLOW_NUL, &foo__repeated_values__descriptor, &protobuf_message, NULL, 0); 327 | ASSERT_ZERO(result); 328 | 329 | Foo__RepeatedValues *repeated_values = (Foo__RepeatedValues *)protobuf_message; 330 | 331 | ASSERT(repeated_values->n_value_string == 3); 332 | ASSERT(strlen(repeated_values->value_string[0]) == 7); 333 | ASSERT_STRNCMP((const char *)repeated_values->value_string[0], "qwerty ", strlen(repeated_values->value_string[0])); 334 | 335 | char *json_string; 336 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 337 | ASSERT_ZERO(result); 338 | ASSERT(json_string); 339 | 340 | const char *expected_json_string = \ 341 | "{\n" 342 | " \"value_string\": [\n" 343 | " \"qwerty \",\n" /* Note: \0-byte terminated string */ 344 | " \"qwerty \",\n" /* Note: \0-byte terminated string */ 345 | " \"\"\n" /* Note: \0-byte terminated string */ 346 | " ]\n" 347 | "}" 348 | ; 349 | 350 | ASSERT_STRCMP( 351 | json_string, 352 | expected_json_string 353 | ); 354 | 355 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 356 | free(json_string); 357 | 358 | RETURN_OK(); 359 | } 360 | 361 | TEST_IMPL(reversible__bytes) { 362 | int result; 363 | 364 | const char *initial_json_string = \ 365 | "{\n" 366 | " \"value_bytes\": [\n" 367 | " \"AA==\",\n" /* "\0" */ 368 | " \"cXdlcnR5IAAgMTIzNA==\",\n" /* "qwerty \0 12345" */ 369 | " \"cXdlcnR5IA==\",\n" /* "qwerty \0" */ 370 | " \"ACAxMjM0NQ==\"\n" /* "\0 12345" */ 371 | " ]\n" 372 | "}" 373 | ; 374 | 375 | ProtobufCMessage *protobuf_message; 376 | 377 | result = json2protobuf_string((char *)initial_json_string, JSON_ALLOW_NUL, &foo__repeated_values__descriptor, &protobuf_message, NULL, 0); 378 | ASSERT_ZERO(result); 379 | 380 | Foo__RepeatedValues *repeated_values = (Foo__RepeatedValues *)protobuf_message; 381 | 382 | ASSERT(repeated_values->n_value_bytes == 4); 383 | /* 0 */ 384 | ASSERT(repeated_values->value_bytes[0].len == 1); 385 | ASSERT_STRNCMP((const char *)repeated_values->value_bytes[0].data, "\0", repeated_values->value_bytes[0].len); 386 | /* 1 */ 387 | ASSERT(repeated_values->value_bytes[1].len == 13); 388 | ASSERT_STRNCMP((const char *)repeated_values->value_bytes[1].data, "qwerty \0 12345", repeated_values->value_bytes[1].len); 389 | /* 0 */ 390 | ASSERT(repeated_values->value_bytes[2].len == 7); 391 | ASSERT_STRNCMP((const char *)repeated_values->value_bytes[2].data, "qwerty \0", repeated_values->value_bytes[2].len); 392 | /* 0 */ 393 | ASSERT(repeated_values->value_bytes[3].len == 7); 394 | ASSERT_STRNCMP((const char *)repeated_values->value_bytes[3].data, "\0 12345", repeated_values->value_bytes[3].len); 395 | 396 | char *json_string; 397 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 398 | ASSERT_ZERO(result); 399 | ASSERT(json_string); 400 | 401 | const char *expected_json_string = \ 402 | "{\n" 403 | " \"value_bytes\": [\n" 404 | " \"AA==\",\n" /* "\0" */ 405 | " \"cXdlcnR5IAAgMTIzNA==\",\n" /* "qwerty \0 12345" */ 406 | " \"cXdlcnR5IA==\",\n" /* "qwerty \0" */ 407 | " \"ACAxMjM0NQ==\"\n" /* "\0 12345" */ 408 | " ]\n" 409 | "}" 410 | ; 411 | 412 | ASSERT_STRCMP( 413 | json_string, 414 | expected_json_string 415 | ); 416 | 417 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 418 | free(json_string); 419 | 420 | RETURN_OK(); 421 | } 422 | 423 | TEST_IMPL(reversible__oneof_none) { 424 | int result; 425 | 426 | const char *initial_json_string = \ 427 | "{}" 428 | ; 429 | 430 | ProtobufCMessage *protobuf_message; 431 | 432 | result = json2protobuf_string((char *)initial_json_string, JSON_ALLOW_NUL, &foo__something__descriptor, &protobuf_message, NULL, 0); 433 | ASSERT_ZERO(result); 434 | 435 | Foo__Something *something = (Foo__Something *)protobuf_message; 436 | 437 | ASSERT_EQUALS(something->something_case, FOO__SOMETHING__SOMETHING__NOT_SET); 438 | ASSERT(something->oneof_string == NULL); 439 | 440 | char *json_string; 441 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 442 | ASSERT_ZERO(result); 443 | ASSERT(json_string); 444 | 445 | const char *expected_json_string = initial_json_string; 446 | 447 | ASSERT_STRCMP( 448 | json_string, 449 | expected_json_string 450 | ); 451 | 452 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 453 | free(json_string); 454 | 455 | RETURN_OK(); 456 | } 457 | 458 | TEST_IMPL(reversible__oneof_one) { 459 | int result; 460 | 461 | const char *initial_json_string = \ 462 | "{\n" 463 | " \"oneof_string\": \"hello\"\n" 464 | "}" 465 | ; 466 | 467 | ProtobufCMessage *protobuf_message; 468 | 469 | result = json2protobuf_string((char *)initial_json_string, JSON_ALLOW_NUL, &foo__something__descriptor, &protobuf_message, NULL, 0); 470 | ASSERT_ZERO(result); 471 | 472 | Foo__Something *something = (Foo__Something *)protobuf_message; 473 | 474 | ASSERT_EQUALS(something->something_case, FOO__SOMETHING__SOMETHING_ONEOF_STRING); 475 | ASSERT_STRCMP(something->oneof_string, "hello"); 476 | 477 | char *json_string; 478 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 479 | ASSERT_ZERO(result); 480 | ASSERT(json_string); 481 | 482 | const char *expected_json_string = initial_json_string; 483 | 484 | ASSERT_STRCMP( 485 | json_string, 486 | expected_json_string 487 | ); 488 | 489 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 490 | free(json_string); 491 | 492 | RETURN_OK(); 493 | } 494 | 495 | TEST_IMPL(reversible__oneof_other) { 496 | int result; 497 | 498 | const char *initial_json_string = \ 499 | "{\n" 500 | " \"oneof_bytes\": \"d29ybGQ=\"\n" 501 | "}" 502 | ; 503 | 504 | ProtobufCMessage *protobuf_message; 505 | 506 | result = json2protobuf_string((char *)initial_json_string, JSON_ALLOW_NUL, &foo__something__descriptor, &protobuf_message, NULL, 0); 507 | ASSERT_ZERO(result); 508 | 509 | Foo__Something *something = (Foo__Something *)protobuf_message; 510 | 511 | ASSERT_EQUALS(something->something_case, FOO__SOMETHING__SOMETHING_ONEOF_BYTES); 512 | ASSERT_EQUALS((int)something->oneof_bytes.len, 5); 513 | ASSERT_STRCMP((const char*)something->oneof_bytes.data, "world"); 514 | 515 | char *json_string; 516 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 517 | ASSERT_ZERO(result); 518 | ASSERT(json_string); 519 | 520 | const char *expected_json_string = initial_json_string; 521 | 522 | ASSERT_STRCMP( 523 | json_string, 524 | expected_json_string 525 | ); 526 | 527 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 528 | free(json_string); 529 | 530 | RETURN_OK(); 531 | } 532 | 533 | TEST_IMPL(reversible__oneof_both_first) { 534 | #if JANSSON_VERSION_HEX < 0x020800 535 | RETURN_SKIP("behavior for multiple oneof values can only be guaranteed for Jansson >= 2.8"); 536 | #else 537 | int result; 538 | 539 | const char *initial_json_string = \ 540 | "{\n" 541 | " \"oneof_string\": \"hello\",\n" 542 | " \"oneof_bytes\": \"d29ybGQ=\"\n" 543 | "}" 544 | ; 545 | 546 | ProtobufCMessage *protobuf_message; 547 | 548 | result = json2protobuf_string((char *)initial_json_string, JSON_ALLOW_NUL, &foo__something__descriptor, &protobuf_message, NULL, 0); 549 | ASSERT_ZERO(result); 550 | 551 | Foo__Something *something = (Foo__Something *)protobuf_message; 552 | 553 | // last input wins, irrespective of order in proto definition 554 | ASSERT_EQUALS(something->something_case, FOO__SOMETHING__SOMETHING_ONEOF_BYTES); 555 | ASSERT_EQUALS((int)something->oneof_bytes.len, 5); 556 | ASSERT_STRCMP((const char*)something->oneof_bytes.data, "world"); 557 | 558 | char *json_string; 559 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 560 | ASSERT_ZERO(result); 561 | ASSERT(json_string); 562 | 563 | const char *expected_json_string = \ 564 | "{\n" 565 | " \"oneof_bytes\": \"d29ybGQ=\"\n" 566 | "}" 567 | ; 568 | 569 | ASSERT_STRCMP( 570 | json_string, 571 | expected_json_string 572 | ); 573 | 574 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 575 | free(json_string); 576 | 577 | RETURN_OK(); 578 | #endif 579 | } 580 | 581 | TEST_IMPL(reversible__oneof_both_second) { 582 | #if JANSSON_VERSION_HEX < 0x020800 583 | RETURN_SKIP("behavior for multiple oneof values can only be guaranteed for Jansson >= 2.8"); 584 | #else 585 | int result; 586 | 587 | const char *initial_json_string = \ 588 | "{\n" 589 | " \"oneof_bytes\": \"d29ybGQ=\",\n" 590 | " \"oneof_string\": \"hello\"\n" 591 | "}" 592 | ; 593 | 594 | ProtobufCMessage *protobuf_message; 595 | 596 | result = json2protobuf_string((char *)initial_json_string, JSON_ALLOW_NUL, &foo__something__descriptor, &protobuf_message, NULL, 0); 597 | ASSERT_ZERO(result); 598 | 599 | Foo__Something *something = (Foo__Something *)protobuf_message; 600 | 601 | // last input wins, irrespective of order in proto definition 602 | ASSERT_EQUALS(something->something_case, FOO__SOMETHING__SOMETHING_ONEOF_STRING); 603 | ASSERT_STRCMP(something->oneof_string, "hello"); 604 | 605 | char *json_string; 606 | result = protobuf2json_string(protobuf_message, TEST_JSON_FLAGS, &json_string, NULL, 0); 607 | ASSERT_ZERO(result); 608 | ASSERT(json_string); 609 | 610 | const char *expected_json_string = \ 611 | "{\n" 612 | " \"oneof_string\": \"hello\"\n" 613 | "}" 614 | ; 615 | 616 | ASSERT_STRCMP( 617 | json_string, 618 | expected_json_string 619 | ); 620 | 621 | protobuf_c_message_free_unpacked(protobuf_message, NULL); 622 | free(json_string); 623 | 624 | RETURN_OK(); 625 | #endif 626 | } 627 | -------------------------------------------------------------------------------- /test/test.proto: -------------------------------------------------------------------------------- 1 | package Foo; 2 | 3 | message Person { 4 | required string name = 1; 5 | required int32 id = 2; 6 | optional string email = 3; 7 | 8 | enum PhoneType { 9 | MOBILE = 0; 10 | HOME = 1; 11 | WORK = 2; 12 | } 13 | 14 | message PhoneNumber { 15 | required string number = 1; 16 | optional PhoneType type = 2 [default = HOME]; 17 | } 18 | 19 | repeated PhoneNumber phone = 4; 20 | } 21 | 22 | enum FizzBuzzType { 23 | FIZZ = 3; 24 | BUZZ = 5; 25 | FIZZBUZZ = 15; 26 | } 27 | 28 | message Bar { 29 | required string string_required = 1; 30 | required string string_required_default = 2 [default = "default value 1"]; 31 | 32 | optional string string_optional = 3; 33 | optional string string_optional_default = 4 [default = "default value 2"]; 34 | 35 | optional bytes bytes_optional = 5; 36 | optional bytes bytes_optional_default = 6 [default = "default value 3"]; 37 | 38 | optional FizzBuzzType enum_optional = 7; 39 | optional FizzBuzzType enum_optional_default = 8 [default = FIZZBUZZ]; 40 | } 41 | 42 | message RepeatedValues { 43 | repeated int32 value_int32 = 1; 44 | repeated sint32 value_sint32 = 2; 45 | repeated sfixed32 value_sfixed32 = 3; 46 | repeated uint32 value_uint32 = 4; 47 | repeated fixed32 value_fixed32 = 5; 48 | repeated int64 value_int64 = 6; 49 | repeated sint64 value_sint64 = 7; 50 | repeated sfixed64 value_sfixed64 = 8; 51 | repeated uint64 value_uint64 = 9; 52 | repeated fixed64 value_fixed64 = 10; 53 | repeated float value_float = 11; 54 | repeated double value_double = 12; 55 | repeated bool value_bool = 13; 56 | 57 | repeated FizzBuzzType value_enum = 14; 58 | repeated string value_string = 15; 59 | repeated bytes value_bytes = 16; 60 | 61 | repeated Person value_message = 17; 62 | } 63 | 64 | message Something { 65 | oneof something { 66 | string oneof_string = 11; 67 | bytes oneof_bytes = 22; 68 | } 69 | } 70 | --------------------------------------------------------------------------------