Compare commits
168 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3d5798896 | ||
|
|
cf1842dc6f | ||
|
|
030d0c14cc | ||
|
|
02cd3eec37 | ||
|
|
bdb59f09de | ||
|
|
5f783fff11 | ||
|
|
7969af6434 | ||
|
|
0bb1843925 | ||
|
|
5ea4fad263 | ||
|
|
1749de02f8 | ||
|
|
748f4bfd4f | ||
|
|
bea1d102fd | ||
|
|
d441fa05b3 | ||
|
|
3fe72cf2b8 | ||
|
|
d8d0ae66d3 | ||
|
|
9d07917feb | ||
|
|
ad5abf4c5b | ||
|
|
2c45ad7816 | ||
|
|
6405fd15e3 | ||
|
|
b44c917be9 | ||
|
|
2d3520e0b9 | ||
|
|
2e118df0cd | ||
|
|
412f4f7d62 | ||
|
|
0aea75fbda | ||
|
|
0c0dd4a5b0 | ||
|
|
dd4cb5400f | ||
|
|
1ea72f8260 | ||
|
|
e78bc42362 | ||
|
|
6f271e511f | ||
|
|
bee069b4e7 | ||
|
|
1e0bd24f2c | ||
|
|
5cf56fa4fa | ||
|
|
3f349a4258 | ||
|
|
40e3781e9b | ||
|
|
b056d7cb74 | ||
|
|
1f422b586a | ||
|
|
899529e866 | ||
|
|
2837aac23e | ||
|
|
c66d95d116 | ||
|
|
cb6df3ffad | ||
|
|
695d8a01a9 | ||
|
|
024f690289 | ||
|
|
039b1cc653 | ||
|
|
c0ff1fef9b | ||
|
|
2302f4f0b2 | ||
|
|
c45dc12fd7 | ||
|
|
ed8dc53699 | ||
|
|
c26d53f0d7 | ||
|
|
29f312dd69 | ||
|
|
72e6e23523 | ||
|
|
501046247d | ||
|
|
68cd0d4a11 | ||
|
|
56b819bfbc | ||
|
|
605422c60a | ||
|
|
cbbcc91498 | ||
|
|
b47d0e34ca | ||
|
|
bc12c69b54 | ||
|
|
1d42c9bc60 | ||
|
|
b71db93e03 | ||
|
|
6d5a7c8c40 | ||
|
|
8c1ed3ab82 | ||
|
|
f16dd7e028 | ||
|
|
08770fc246 | ||
|
|
0ca8587acc | ||
|
|
88e38d042f | ||
|
|
6a746a230a | ||
|
|
1e999352d3 | ||
|
|
6815d96617 | ||
|
|
61921498d0 | ||
|
|
4758d62fd4 | ||
|
|
1bc1a9748a | ||
|
|
de36476092 | ||
|
|
983a4cd286 | ||
|
|
b6abef94ff | ||
|
|
3d66766231 | ||
|
|
924122904e | ||
|
|
87691a86e5 | ||
|
|
8aa324fdc8 | ||
|
|
3c4d309f62 | ||
|
|
9bf531ca05 | ||
|
|
80354bdb06 | ||
|
|
fe2e0956ad | ||
|
|
cf71f3d627 | ||
|
|
da551c753f | ||
|
|
ae4681b787 | ||
|
|
c5a09a32a9 | ||
|
|
4785070ad3 | ||
|
|
0e0cd5bae5 | ||
|
|
44512f643e | ||
|
|
331c18d09a | ||
|
|
4fff92140e | ||
|
|
fc1d4b07df | ||
|
|
bd073343fa | ||
|
|
7c722dca5f | ||
|
|
4fce9cce86 | ||
|
|
415962da34 | ||
|
|
b41264d164 | ||
|
|
0f271dcf63 | ||
|
|
ead389aba2 | ||
|
|
698dc528f4 | ||
|
|
3dc6339025 | ||
|
|
15592c50f6 | ||
|
|
efb5e1bc93 | ||
|
|
ace5047782 | ||
|
|
03f23738bb | ||
|
|
24dbf29360 | ||
|
|
b6974ecbc9 | ||
|
|
12b2daccf3 | ||
|
|
f8d0c47bdb | ||
|
|
9f6fa94c91 | ||
|
|
123bb1af7b | ||
|
|
cf862d0fed | ||
|
|
57d105d498 | ||
|
|
5986edba1d | ||
|
|
c6e1a281f9 | ||
|
|
4f58695ed3 | ||
|
|
3facca4792 | ||
|
|
cf48ea8175 | ||
|
|
702fd95af3 | ||
|
|
94117a5d23 | ||
|
|
c3bd4463be | ||
|
|
9d7e8f1175 | ||
|
|
4047de4f6e | ||
|
|
49b9336558 | ||
|
|
d04a2aeccf | ||
|
|
33e01ae087 | ||
|
|
a09defec4c | ||
|
|
21c02cd3e5 | ||
|
|
5a36b71a80 | ||
|
|
9041570eba | ||
|
|
71b05fd4c2 | ||
|
|
598b609c45 | ||
|
|
b0e5209bde | ||
|
|
7fd536d7e0 | ||
|
|
be0951dfa4 | ||
|
|
86be961bb5 | ||
|
|
bb60d6def5 | ||
|
|
896e52255b | ||
|
|
12acc57967 | ||
|
|
6b9b57be22 | ||
|
|
d19f3ae890 | ||
|
|
e65ea3a45b | ||
|
|
87f77274de | ||
|
|
dded751757 | ||
|
|
d7b5545748 | ||
|
|
0747669972 | ||
|
|
f09bdef15e | ||
|
|
7119a16b5f | ||
|
|
cc514583cc | ||
|
|
021b174ee1 | ||
|
|
99cd9af7d5 | ||
|
|
8656386c4f | ||
|
|
c866abd842 | ||
|
|
1f5538f79d | ||
|
|
9ed906758e | ||
|
|
2f712c7456 | ||
|
|
d00ca18ac2 | ||
|
|
6cdb6894d4 | ||
|
|
ee0c920dff | ||
|
|
af6d5d3d7c | ||
|
|
cf9d57d56c | ||
|
|
4d95639001 | ||
|
|
3d3bfc6a4d | ||
|
|
41e2837df1 | ||
|
|
ecd5678527 | ||
|
|
28b9ba4334 | ||
|
|
b182ced1d6 | ||
|
|
fe18403935 |
54
.github/CONTRIBUTING.md
vendored
Normal file
54
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
Contribution Guidelines
|
||||
=======================
|
||||
|
||||
Contributions to cJSON are welcome. If you find a bug or want to improve cJSON in another way, pull requests are appreciated.
|
||||
|
||||
For bigger changes, in order to avoid wasted effort, please open an issue to discuss the technical details before creating a pull request.
|
||||
|
||||
The further sections explain the process in more detail and provides some guidelines on how contributions should look like.
|
||||
|
||||
Branches
|
||||
--------
|
||||
There are two branches to be aware of, the `master` and the `develop` branch. The `master` branch is reserved for the latest release, so only make pull requests to the `master` branch for small bug- or security fixes (these are usually just a few lines). In all other cases, please make a pull request to the `develop` branch.
|
||||
|
||||
Coding Style
|
||||
------------
|
||||
The coding style has been discussed in [#24](https://github.com/DaveGamble/cJSON/issues/24). The basics are:
|
||||
|
||||
* Use 4 tabs for indentation
|
||||
* No oneliners (conditions, loops, variable declarations ...)
|
||||
* Always use parenthesis for control structures
|
||||
* Don't implicitly rely on operator precedence, use round brackets in expressions. e.g. `(a > b) && (c < d)` instead of `a>b && c<d`
|
||||
* opening curly braces start in the next line
|
||||
* use spaces around operators
|
||||
* lines should not have trailing whitespace
|
||||
* use spaces between function parameters
|
||||
* use pronouncable variable names, not just a combination of letters
|
||||
|
||||
Example:
|
||||
|
||||
```c
|
||||
/* calculate the new length of the string in a printbuffer and update the offset */
|
||||
static void update_offset(printbuffer * const buffer)
|
||||
{
|
||||
const unsigned char *buffer_pointer = NULL;
|
||||
if ((buffer == NULL) || (buffer->buffer == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
buffer_pointer = buffer->buffer + buffer->offset;
|
||||
|
||||
buffer->offset += strlen((const char*)buffer_pointer);
|
||||
}
|
||||
```
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
cJSON uses the [Unity](https://github.com/ThrowTheSwitch/Unity) library for unit tests. The tests are located in the `tests` directory. In order to add a new test, either add it to one of the existing files (if it fits) or add a new C file for the test. That new file has to be added to the list of tests in `tests/CMakeLists.txt`.
|
||||
|
||||
All new features have to be covered by unit tests.
|
||||
|
||||
Other Notes
|
||||
-----------
|
||||
* Internal functions are to be declared static.
|
||||
* Wrap the return type of external function in the `CJSON_PUBLIC` macro.
|
||||
28
.travis.yml
Normal file
28
.travis.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
dist: trusty
|
||||
sudo: false
|
||||
language: c
|
||||
env:
|
||||
matrix:
|
||||
- VALGRIND=On SANITIZERS=Off
|
||||
- VALGRIND=Off SANITIZERS=Off
|
||||
- VALGRIND=Off SANITIZERS=On
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- valgrind
|
||||
- libasan0
|
||||
- lib32asan0
|
||||
# currently not supported on travis:
|
||||
# - libasan1
|
||||
# - libasan2
|
||||
# - libubsan0
|
||||
- llvm
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_VALGRIND="${VALGRIND}" -DENABLE_SANITIZERS="${SANITIZERS}"
|
||||
- make
|
||||
- make test CTEST_OUTPUT_ON_FAILURE=On
|
||||
@@ -1,24 +1,87 @@
|
||||
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
subdirs(tests fuzzing)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
project(cJSON C)
|
||||
|
||||
set(PROJECT_VERSION_MAJOR 1)
|
||||
set(PROJECT_VERSION_MINOR 2)
|
||||
set(PROJECT_VERSION_MINOR 4)
|
||||
set(PROJECT_VERSION_PATCH 1)
|
||||
set(CJSON_VERSION_SO 1)
|
||||
set(CJSON_UTILS_VERSION_SO 1)
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
|
||||
|
||||
set(custom_compiler_flags)
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
option(ENABLE_CUSTOM_COMPILER_FLAGS "Enables custom compiler flags for Clang and GCC" ON)
|
||||
if (ENABLE_CUSTOM_COMPILER_FLAGS)
|
||||
if(("${CMAKE_C_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang"))
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c89 -pedantic -Wall -Wextra -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wstrict-overflow=2 -Wcast-qual -Wc++-compat")
|
||||
endif()
|
||||
list(APPEND custom_compiler_flags
|
||||
-std=c89
|
||||
-pedantic
|
||||
-Wall
|
||||
-Wextra
|
||||
-Werror
|
||||
-Wstrict-prototypes
|
||||
-Wwrite-strings
|
||||
-Wshadow
|
||||
-Winit-self
|
||||
-Wcast-align
|
||||
-Wformat=2
|
||||
-Wmissing-prototypes
|
||||
-Wstrict-overflow=2
|
||||
-Wcast-qual
|
||||
-Wundef
|
||||
-Wswitch-default
|
||||
-Wconversion
|
||||
-Wc++-compat
|
||||
-fstack-protector-strong
|
||||
-Wcomma
|
||||
-Wdouble-promotion
|
||||
-Wparentheses
|
||||
)
|
||||
endif()
|
||||
|
||||
option(ENABLE_SANITIZERS "Enables AddressSanitizer and UndefinedBehaviorSanitizer." OFF)
|
||||
if (ENABLE_SANITIZERS)
|
||||
list(APPEND custom_compiler_flags
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize=address
|
||||
-fsanitize=undefined
|
||||
-fsanitize=float-divide-by-zero
|
||||
-fsanitize=float-cast-overflow
|
||||
-fsanitize-address-use-after-scope
|
||||
-fsanitize=integer
|
||||
-01
|
||||
-fno-sanitize-recover
|
||||
)
|
||||
endif()
|
||||
|
||||
option(ENABLE_PUBLIC_SYMBOLS "Export library symbols." On)
|
||||
if (ENABLE_PUBLIC_SYMBOLS)
|
||||
list(APPEND custom_compiler_flags -fvisibility=hidden)
|
||||
add_definitions(-DCJSON_EXPORT_SYMBOLS -DCJSON_API_VISIBILITY)
|
||||
endif()
|
||||
option(ENABLE_HIDDEN_SYMBOLS "Hide library symbols." Off)
|
||||
if (ENABLE_HIDDEN_SYMBOLS)
|
||||
add_definitions(-DCJSON_HIDE_SYMBOLS -UCJSON_API_VISIBILITY)
|
||||
endif()
|
||||
|
||||
# apply custom compiler flags
|
||||
foreach(compiler_flag ${custom_compiler_flags})
|
||||
#remove problematic characters
|
||||
string(REGEX REPLACE "[^a-zA-Z0-9]" "" current_variable ${compiler_flag})
|
||||
|
||||
CHECK_C_COMPILER_FLAG(${compiler_flag} "FLAG_SUPPORTED_${current_variable}")
|
||||
if (FLAG_SUPPORTED_${current_variable})
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${compiler_flag}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
#variables for pkg-config
|
||||
set(prefix "${CMAKE_INSTALL_PREFIX}")
|
||||
set(libdir "${CMAKE_INSTALL_LIBDIR}")
|
||||
@@ -98,6 +161,8 @@ install(FILES ${PROJECT_BINARY_DIR}/cJSONConfig.cmake
|
||||
|
||||
option(ENABLE_CJSON_TEST "Enable building cJSON test" ON)
|
||||
if(ENABLE_CJSON_TEST)
|
||||
enable_testing()
|
||||
|
||||
set(TEST_CJSON cJSON_test)
|
||||
add_executable("${TEST_CJSON}" test.c)
|
||||
target_link_libraries("${TEST_CJSON}" "${CJSON_LIB}")
|
||||
|
||||
@@ -21,6 +21,7 @@ Contributors
|
||||
* [Linus Wallgren](https://github.com/ecksun)
|
||||
* [Max Bruckner](https://github.com/FSMaxB)
|
||||
* Mike Pontillo
|
||||
* [Mike Jerris](https://github.com/mjerris)
|
||||
* Paulo Antonio Alvarez
|
||||
* [Rafael Leal Dias](https://github.com/rafaeldias)
|
||||
* [Rod Vagg](https://github.com/rvagg)
|
||||
|
||||
4
Makefile
4
Makefile
@@ -10,7 +10,7 @@ UTILS_TEST_SRC = cJSON.c cJSON_Utils.c test_utils.c
|
||||
|
||||
LDLIBS = -lm
|
||||
|
||||
LIBVERSION = 1.2.1
|
||||
LIBVERSION = 1.4.1
|
||||
CJSON_SOVERSION = 1
|
||||
UTILS_SOVERSION = 1
|
||||
|
||||
@@ -23,7 +23,7 @@ INSTALL_LIBRARY_PATH = $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH)
|
||||
|
||||
INSTALL ?= cp -a
|
||||
|
||||
R_CFLAGS = -fPIC -std=c89 -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wstrict-overflow=2 -Wcast-qual -Wc++-compat $(CFLAGS)
|
||||
R_CFLAGS = -fPIC -std=c89 -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wstrict-overflow=2 -Wcast-qual -Wc++-compat -Wundef -Wswitch-default -Wconversion -fstack-protector-strong $(CFLAGS)
|
||||
|
||||
uname := $(shell sh -c 'uname -s 2>/dev/null || echo false')
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@ You can change the build process with a list of different options that you can p
|
||||
* `-DENABLE_CJSON_UTILS=On`: Enable building cJSON_Utils. (off by default)
|
||||
* `-DENABLE_TARGET_EXPORT=On`: Enable the export of CMake targets. Turn off if it makes problems. (on by default)
|
||||
* `-DENABLE_CUSTOM_COMPILER_FLAGS=On`: Enable custom compiler flags (currently for Clang and GCC). Turn off if it makes problems. (on by default)
|
||||
* `-DENABLE_VALGRIND=On`: Run tests with [valgrind](http://valgrind.org). (off by default)
|
||||
* `-DENABLE_SANITIZERS=On`: Compile cJSON with [AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) enabled (if possible). (off by default)
|
||||
* `-DBUILD_SHARED_LIBS=On`: Build the shared libraries. (on by default)
|
||||
* `-DCMAKE_INSTALL_PREFIX=/usr`: Set a prefix for the installation.
|
||||
|
||||
|
||||
149
cJSON.h
149
cJSON.h
@@ -30,15 +30,13 @@ extern "C"
|
||||
|
||||
/* project version */
|
||||
#define CJSON_VERSION_MAJOR 1
|
||||
#define CJSON_VERSION_MINOR 2
|
||||
#define CJSON_VERSION_MINOR 4
|
||||
#define CJSON_VERSION_PATCH 1
|
||||
|
||||
/* returns the version of cJSON as a string */
|
||||
extern const char* cJSON_Version(void);
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* cJSON Types: */
|
||||
#define cJSON_Invalid (0)
|
||||
#define cJSON_False (1 << 0)
|
||||
#define cJSON_True (1 << 1)
|
||||
#define cJSON_NULL (1 << 2)
|
||||
@@ -80,84 +78,139 @@ typedef struct cJSON_Hooks
|
||||
void (*free_fn)(void *ptr);
|
||||
} cJSON_Hooks;
|
||||
|
||||
typedef int cJSON_bool;
|
||||
|
||||
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
||||
#define __WINDOWS__
|
||||
#endif
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options:
|
||||
|
||||
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
|
||||
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols
|
||||
|
||||
For *nix builds that support visibility attribute, you can define similar behavior by
|
||||
|
||||
setting default visibility to hidden by adding
|
||||
-fvisibility=hidden (for gcc)
|
||||
or
|
||||
-xldscope=hidden (for sun cc)
|
||||
to CFLAGS
|
||||
|
||||
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
|
||||
|
||||
*/
|
||||
|
||||
#if defined(CJSON_HIDE_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) type __stdcall
|
||||
#elif defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall
|
||||
#else
|
||||
#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall
|
||||
#endif
|
||||
#else /* !WIN32 */
|
||||
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
|
||||
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
||||
#else
|
||||
#define CJSON_PUBLIC(type) type
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* returns the version of cJSON as a string */
|
||||
CJSON_PUBLIC(const char*) cJSON_Version(void);
|
||||
|
||||
/* Supply malloc, realloc and free functions to cJSON */
|
||||
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
|
||||
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
|
||||
extern cJSON *cJSON_Parse(const char *value);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
|
||||
extern char *cJSON_Print(const cJSON *item);
|
||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
|
||||
extern char *cJSON_PrintUnformatted(const cJSON *item);
|
||||
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||
extern char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt);
|
||||
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||
/* Render a cJSON entity to text using a buffer already allocated in memory with length buf_len. Returns 1 on success and 0 on failure. */
|
||||
extern int cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const int fmt);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
extern void cJSON_Delete(cJSON *c);
|
||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
extern int cJSON_GetArraySize(const cJSON *array);
|
||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
|
||||
extern cJSON *cJSON_GetArrayItem(const cJSON *array, int item);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item);
|
||||
/* Get item "string" from object. Case insensitive. */
|
||||
extern cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string);
|
||||
extern int cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
extern const char *cJSON_GetErrorPtr(void);
|
||||
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||
|
||||
/* These functions check the type of an item */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
|
||||
|
||||
/* These calls create a cJSON item of the appropriate type. */
|
||||
extern cJSON *cJSON_CreateNull(void);
|
||||
extern cJSON *cJSON_CreateTrue(void);
|
||||
extern cJSON *cJSON_CreateFalse(void);
|
||||
extern cJSON *cJSON_CreateBool(int b);
|
||||
extern cJSON *cJSON_CreateNumber(double num);
|
||||
extern cJSON *cJSON_CreateString(const char *string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
||||
/* raw json */
|
||||
extern cJSON *cJSON_CreateRaw(const char *raw);
|
||||
extern cJSON *cJSON_CreateArray(void);
|
||||
extern cJSON *cJSON_CreateObject(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
||||
|
||||
/* These utilities create an Array of count items. */
|
||||
extern cJSON *cJSON_CreateIntArray(const int *numbers, int count);
|
||||
extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||
extern cJSON *cJSON_CreateStringArray(const char **strings, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count);
|
||||
|
||||
/* Append item to the specified array/object. */
|
||||
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
extern void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
||||
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
|
||||
* writing to `item->string` */
|
||||
extern void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||
CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||
CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||
|
||||
/* Remove/Detatch items from Arrays/Objects. */
|
||||
extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||
extern void cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||
extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||
extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
|
||||
/* Update array items. */
|
||||
extern void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
|
||||
/* Duplicate a cJSON item */
|
||||
extern cJSON *cJSON_Duplicate(const cJSON *item, int recurse);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
|
||||
extern cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
|
||||
extern void cJSON_Minify(char *json);
|
||||
CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
||||
|
||||
/* Macros for creating things quickly. */
|
||||
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
|
||||
@@ -169,11 +222,13 @@ extern void cJSON_Minify(char *json);
|
||||
#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s))
|
||||
|
||||
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||
#define cJSON_SetIntValue(object,val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val))
|
||||
#define cJSON_SetNumberValue(object,val) ((object) ? (object)->valueint = (object)->valuedouble = (val) : (val))
|
||||
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
||||
/* helper for the cJSON_SetNumberValue macro */
|
||||
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
|
||||
|
||||
/* Macro for iterating over an array */
|
||||
#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next)
|
||||
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
254
cJSON_Utils.c
254
cJSON_Utils.c
@@ -1,16 +1,20 @@
|
||||
#pragma GCC visibility push(default)
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#pragma GCC visibility pop
|
||||
|
||||
#include "cJSON_Utils.h"
|
||||
|
||||
static char* cJSONUtils_strdup(const char* str)
|
||||
static unsigned char* cJSONUtils_strdup(const unsigned char* str)
|
||||
{
|
||||
size_t len = 0;
|
||||
char *copy = NULL;
|
||||
unsigned char *copy = NULL;
|
||||
|
||||
len = strlen(str) + 1;
|
||||
if (!(copy = (char*)malloc(len)))
|
||||
len = strlen((const char*)str) + 1;
|
||||
if (!(copy = (unsigned char*)malloc(len)))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -19,7 +23,7 @@ static char* cJSONUtils_strdup(const char* str)
|
||||
return copy;
|
||||
}
|
||||
|
||||
static int cJSONUtils_strcasecmp(const char *s1, const char *s2)
|
||||
static int cJSONUtils_strcasecmp(const unsigned char *s1, const unsigned char *s2)
|
||||
{
|
||||
if (!s1)
|
||||
{
|
||||
@@ -29,7 +33,7 @@ static int cJSONUtils_strcasecmp(const char *s1, const char *s2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)
|
||||
for(; tolower(*s1) == tolower(*s2); (void)++s1, ++s2)
|
||||
{
|
||||
if(*s1 == 0)
|
||||
{
|
||||
@@ -37,17 +41,17 @@ static int cJSONUtils_strcasecmp(const char *s1, const char *s2)
|
||||
}
|
||||
}
|
||||
|
||||
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
|
||||
return tolower(*s1) - tolower(*s2);
|
||||
}
|
||||
|
||||
/* JSON Pointer implementation: */
|
||||
static int cJSONUtils_Pstrcasecmp(const char *a, const char *e)
|
||||
static int cJSONUtils_Pstrcasecmp(const unsigned char *a, const unsigned char *e)
|
||||
{
|
||||
if (!a || !e)
|
||||
{
|
||||
return (a == e) ? 0 : 1; /* both NULL? */
|
||||
}
|
||||
for (; *a && *e && (*e != '/'); a++, e++) /* compare until next '/' */
|
||||
for (; *a && *e && (*e != '/'); (void)a++, e++) /* compare until next '/' */
|
||||
{
|
||||
if (*e == '~')
|
||||
{
|
||||
@@ -76,10 +80,10 @@ static int cJSONUtils_Pstrcasecmp(const char *a, const char *e)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cJSONUtils_PointerEncodedstrlen(const char *s)
|
||||
static size_t cJSONUtils_PointerEncodedstrlen(const unsigned char *s)
|
||||
{
|
||||
int l = 0;
|
||||
for (; *s; s++, l++)
|
||||
size_t l = 0;
|
||||
for (; *s; (void)s++, l++)
|
||||
{
|
||||
if ((*s == '~') || (*s == '/'))
|
||||
{
|
||||
@@ -90,7 +94,7 @@ static int cJSONUtils_PointerEncodedstrlen(const char *s)
|
||||
return l;
|
||||
}
|
||||
|
||||
static void cJSONUtils_PointerEncodedstrcpy(char *d, const char *s)
|
||||
static void cJSONUtils_PointerEncodedstrcpy(unsigned char *d, const unsigned char *s)
|
||||
{
|
||||
for (; *s; s++)
|
||||
{
|
||||
@@ -113,42 +117,49 @@ static void cJSONUtils_PointerEncodedstrcpy(char *d, const char *s)
|
||||
*d = '\0';
|
||||
}
|
||||
|
||||
char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
|
||||
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
|
||||
{
|
||||
int type = object->type;
|
||||
int c = 0;
|
||||
size_t c = 0;
|
||||
cJSON *obj = 0;
|
||||
|
||||
if (object == target)
|
||||
{
|
||||
/* found */
|
||||
return cJSONUtils_strdup("");
|
||||
return (char*)cJSONUtils_strdup((const unsigned char*)"");
|
||||
}
|
||||
|
||||
/* recursively search all children of the object */
|
||||
for (obj = object->child; obj; obj = obj->next, c++)
|
||||
for (obj = object->child; obj; (void)(obj = obj->next), c++)
|
||||
{
|
||||
char *found = cJSONUtils_FindPointerFromObjectTo(obj, target);
|
||||
unsigned char *found = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(obj, target);
|
||||
if (found)
|
||||
{
|
||||
if ((type & 0xFF) == cJSON_Array)
|
||||
if (cJSON_IsArray(object))
|
||||
{
|
||||
/* reserve enough memory for a 64 bit integer + '/' and '\0' */
|
||||
char *ret = (char*)malloc(strlen(found) + 23);
|
||||
sprintf(ret, "/%d%s", c, found); /* /<array_index><path> */
|
||||
free(found);
|
||||
|
||||
return ret;
|
||||
}
|
||||
else if ((type & 0xFF) == cJSON_Object)
|
||||
unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + 23);
|
||||
/* check if conversion to unsigned long is valid
|
||||
* This should be eliminated at compile time by dead code elimination
|
||||
* if size_t is an alias of unsigned long, or if it is bigger */
|
||||
if (c > ULONG_MAX)
|
||||
{
|
||||
char *ret = (char*)malloc(strlen(found) + cJSONUtils_PointerEncodedstrlen(obj->string) + 2);
|
||||
*ret = '/';
|
||||
cJSONUtils_PointerEncodedstrcpy(ret + 1, obj->string);
|
||||
strcat(ret, found);
|
||||
free(found);
|
||||
return NULL;
|
||||
}
|
||||
sprintf((char*)ret, "/%lu%s", (unsigned long)c, found); /* /<array_index><path> */
|
||||
free(found);
|
||||
|
||||
return ret;
|
||||
return (char*)ret;
|
||||
}
|
||||
else if (cJSON_IsObject(object))
|
||||
{
|
||||
unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + cJSONUtils_PointerEncodedstrlen((unsigned char*)obj->string) + 2);
|
||||
*ret = '/';
|
||||
cJSONUtils_PointerEncodedstrcpy(ret + 1, (unsigned char*)obj->string);
|
||||
strcat((char*)ret, (char*)found);
|
||||
free(found);
|
||||
|
||||
return (char*)ret;
|
||||
}
|
||||
|
||||
/* reached leaf of the tree, found nothing */
|
||||
@@ -161,31 +172,35 @@ char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer)
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer)
|
||||
{
|
||||
/* follow path of the pointer */
|
||||
while ((*pointer++ == '/') && object)
|
||||
{
|
||||
if ((object->type & 0xFF) == cJSON_Array)
|
||||
if (cJSON_IsArray(object))
|
||||
{
|
||||
int which = 0;
|
||||
size_t which = 0;
|
||||
/* parse array index */
|
||||
while ((*pointer >= '0') && (*pointer <= '9'))
|
||||
{
|
||||
which = (10 * which) + (*pointer++ - '0');
|
||||
which = (10 * which) + (size_t)(*pointer++ - '0');
|
||||
}
|
||||
if (*pointer && (*pointer != '/'))
|
||||
{
|
||||
/* not end of string or new path token */
|
||||
return NULL;
|
||||
}
|
||||
object = cJSON_GetArrayItem(object, which);
|
||||
if (which > INT_MAX)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else if ((object->type & 0xFF) == cJSON_Object)
|
||||
object = cJSON_GetArrayItem(object, (int)which);
|
||||
}
|
||||
else if (cJSON_IsObject(object))
|
||||
{
|
||||
object = object->child;
|
||||
/* GetObjectItem. */
|
||||
while (object && cJSONUtils_Pstrcasecmp(object->string, pointer))
|
||||
while (object && cJSONUtils_Pstrcasecmp((unsigned char*)object->string, (const unsigned char*)pointer))
|
||||
{
|
||||
object = object->next;
|
||||
}
|
||||
@@ -205,15 +220,15 @@ cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer)
|
||||
}
|
||||
|
||||
/* JSON Patch implementation. */
|
||||
static void cJSONUtils_InplaceDecodePointerString(char *string)
|
||||
static void cJSONUtils_InplaceDecodePointerString(unsigned char *string)
|
||||
{
|
||||
char *s2 = string;
|
||||
unsigned char *s2 = string;
|
||||
|
||||
if (string == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (; *string; s2++, string++)
|
||||
for (; *string; (void)s2++, string++)
|
||||
{
|
||||
*s2 = (*string != '~')
|
||||
? (*string)
|
||||
@@ -225,10 +240,10 @@ static void cJSONUtils_InplaceDecodePointerString(char *string)
|
||||
*s2 = '\0';
|
||||
}
|
||||
|
||||
static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path)
|
||||
static cJSON *cJSONUtils_PatchDetach(cJSON *object, const unsigned char *path)
|
||||
{
|
||||
char *parentptr = NULL;
|
||||
char *childptr = NULL;
|
||||
unsigned char *parentptr = NULL;
|
||||
unsigned char *childptr = NULL;
|
||||
cJSON *parent = NULL;
|
||||
cJSON *ret = NULL;
|
||||
|
||||
@@ -238,7 +253,7 @@ static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
childptr = strrchr(parentptr, '/'); /* last '/' */
|
||||
childptr = (unsigned char*)strrchr((char*)parentptr, '/'); /* last '/' */
|
||||
if (childptr == NULL)
|
||||
{
|
||||
free(parentptr);
|
||||
@@ -247,7 +262,7 @@ static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path)
|
||||
/* split strings */
|
||||
*childptr++ = '\0';
|
||||
|
||||
parent = cJSONUtils_GetPointer(object, parentptr);
|
||||
parent = cJSONUtils_GetPointer(object, (char*)parentptr);
|
||||
cJSONUtils_InplaceDecodePointerString(childptr);
|
||||
|
||||
if (!parent)
|
||||
@@ -255,13 +270,13 @@ static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path)
|
||||
/* Couldn't find object to remove child from. */
|
||||
ret = NULL;
|
||||
}
|
||||
else if ((parent->type & 0xFF) == cJSON_Array)
|
||||
else if (cJSON_IsArray(parent))
|
||||
{
|
||||
ret = cJSON_DetachItemFromArray(parent, atoi(childptr));
|
||||
ret = cJSON_DetachItemFromArray(parent, atoi((char*)childptr));
|
||||
}
|
||||
else if ((parent->type & 0xFF) == cJSON_Object)
|
||||
else if (cJSON_IsObject(parent))
|
||||
{
|
||||
ret = cJSON_DetachItemFromObject(parent, childptr);
|
||||
ret = cJSON_DetachItemFromObject(parent, (char*)childptr);
|
||||
}
|
||||
free(parentptr);
|
||||
|
||||
@@ -271,7 +286,7 @@ static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path)
|
||||
|
||||
static int cJSONUtils_Compare(cJSON *a, cJSON *b)
|
||||
{
|
||||
if ((a->type & 0xFF) != (b->type & 0xFF))
|
||||
if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
|
||||
{
|
||||
/* mismatched type. */
|
||||
return -1;
|
||||
@@ -285,7 +300,7 @@ static int cJSONUtils_Compare(cJSON *a, cJSON *b)
|
||||
/* string mismatch. */
|
||||
return (strcmp(a->valuestring, b->valuestring) != 0) ? -3 : 0;
|
||||
case cJSON_Array:
|
||||
for (a = a->child, b = b->child; a && b; a = a->next, b = b->next)
|
||||
for ((void)(a = a->child), b = b->child; a && b; (void)(a = a->next), b = b->next)
|
||||
{
|
||||
int err = cJSONUtils_Compare(a, b);
|
||||
if (err)
|
||||
@@ -304,7 +319,7 @@ static int cJSONUtils_Compare(cJSON *a, cJSON *b)
|
||||
{
|
||||
int err = 0;
|
||||
/* compare object keys */
|
||||
if (cJSONUtils_strcasecmp(a->string, b->string))
|
||||
if (cJSONUtils_strcasecmp((unsigned char*)a->string, (unsigned char*)b->string))
|
||||
{
|
||||
/* missing member */
|
||||
return -6;
|
||||
@@ -334,8 +349,8 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
|
||||
cJSON *value = NULL;
|
||||
cJSON *parent = NULL;
|
||||
int opcode = 0;
|
||||
char *parentptr = NULL;
|
||||
char *childptr = NULL;
|
||||
unsigned char *parentptr = NULL;
|
||||
unsigned char *childptr = NULL;
|
||||
|
||||
op = cJSON_GetObjectItem(patch, "op");
|
||||
path = cJSON_GetObjectItem(patch, "path");
|
||||
@@ -381,7 +396,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
|
||||
if ((opcode == 1) || (opcode == 2))
|
||||
{
|
||||
/* Get rid of old. */
|
||||
cJSON_Delete(cJSONUtils_PatchDetach(object, path->valuestring));
|
||||
cJSON_Delete(cJSONUtils_PatchDetach(object, (unsigned char*)path->valuestring));
|
||||
if (opcode == 1)
|
||||
{
|
||||
/* For Remove, this is job done. */
|
||||
@@ -402,7 +417,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
|
||||
if (opcode == 3)
|
||||
{
|
||||
/* move */
|
||||
value = cJSONUtils_PatchDetach(object, from->valuestring);
|
||||
value = cJSONUtils_PatchDetach(object, (unsigned char*)from->valuestring);
|
||||
}
|
||||
if (opcode == 4)
|
||||
{
|
||||
@@ -443,13 +458,13 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
|
||||
/* Now, just add "value" to "path". */
|
||||
|
||||
/* split pointer in parent and child */
|
||||
parentptr = cJSONUtils_strdup(path->valuestring);
|
||||
childptr = strrchr(parentptr, '/');
|
||||
parentptr = cJSONUtils_strdup((unsigned char*)path->valuestring);
|
||||
childptr = (unsigned char*)strrchr((char*)parentptr, '/');
|
||||
if (childptr)
|
||||
{
|
||||
*childptr++ = '\0';
|
||||
}
|
||||
parent = cJSONUtils_GetPointer(object, parentptr);
|
||||
parent = cJSONUtils_GetPointer(object, (char*)parentptr);
|
||||
cJSONUtils_InplaceDecodePointerString(childptr);
|
||||
|
||||
/* add, remove, replace, move, copy, test. */
|
||||
@@ -460,21 +475,21 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
|
||||
cJSON_Delete(value);
|
||||
return 9;
|
||||
}
|
||||
else if ((parent->type & 0xFF) == cJSON_Array)
|
||||
else if (cJSON_IsArray(parent))
|
||||
{
|
||||
if (!strcmp(childptr, "-"))
|
||||
if (!strcmp((char*)childptr, "-"))
|
||||
{
|
||||
cJSON_AddItemToArray(parent, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
cJSON_InsertItemInArray(parent, atoi(childptr), value);
|
||||
cJSON_InsertItemInArray(parent, atoi((char*)childptr), value);
|
||||
}
|
||||
}
|
||||
else if ((parent->type & 0xFF) == cJSON_Object)
|
||||
else if (cJSON_IsObject(parent))
|
||||
{
|
||||
cJSON_DeleteItemFromObject(parent, childptr);
|
||||
cJSON_AddItemToObject(parent, childptr, value);
|
||||
cJSON_DeleteItemFromObject(parent, (char*)childptr);
|
||||
cJSON_AddItemToObject(parent, (char*)childptr, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -485,10 +500,16 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
|
||||
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
|
||||
{
|
||||
int err = 0;
|
||||
if ((patches->type & 0xFF) != cJSON_Array)
|
||||
|
||||
if (patches == NULL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (cJSON_IsArray(patches))
|
||||
{
|
||||
/* malformed patches. */
|
||||
return 1;
|
||||
@@ -509,20 +530,20 @@ int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cJSONUtils_GeneratePatch(cJSON *patches, const char *op, const char *path, const char *suffix, cJSON *val)
|
||||
static void cJSONUtils_GeneratePatch(cJSON *patches, const unsigned char *op, const unsigned char *path, const unsigned char *suffix, cJSON *val)
|
||||
{
|
||||
cJSON *patch = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(patch, "op", cJSON_CreateString(op));
|
||||
cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)op));
|
||||
if (suffix)
|
||||
{
|
||||
char *newpath = (char*)malloc(strlen(path) + cJSONUtils_PointerEncodedstrlen(suffix) + 2);
|
||||
cJSONUtils_PointerEncodedstrcpy(newpath + sprintf(newpath, "%s/", path), suffix);
|
||||
cJSON_AddItemToObject(patch, "path", cJSON_CreateString(newpath));
|
||||
unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + cJSONUtils_PointerEncodedstrlen(suffix) + 2);
|
||||
cJSONUtils_PointerEncodedstrcpy(newpath + sprintf((char*)newpath, "%s/", (const char*)path), suffix);
|
||||
cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)newpath));
|
||||
free(newpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
cJSON_AddItemToObject(patch, "path", cJSON_CreateString(path));
|
||||
cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
|
||||
}
|
||||
if (val)
|
||||
{
|
||||
@@ -531,16 +552,21 @@ static void cJSONUtils_GeneratePatch(cJSON *patches, const char *op, const char
|
||||
cJSON_AddItemToArray(patches, patch);
|
||||
}
|
||||
|
||||
void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val)
|
||||
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val)
|
||||
{
|
||||
cJSONUtils_GeneratePatch(array, op, path, 0, val);
|
||||
cJSONUtils_GeneratePatch(array, (const unsigned char*)op, (const unsigned char*)path, 0, val);
|
||||
}
|
||||
|
||||
static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *from, cJSON *to)
|
||||
static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path, cJSON *from, cJSON *to)
|
||||
{
|
||||
if ((from == NULL) || (to == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((from->type & 0xFF) != (to->type & 0xFF))
|
||||
{
|
||||
cJSONUtils_GeneratePatch(patches, "replace", path, 0, to);
|
||||
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -549,37 +575,53 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *f
|
||||
case cJSON_Number:
|
||||
if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble))
|
||||
{
|
||||
cJSONUtils_GeneratePatch(patches, "replace", path, 0, to);
|
||||
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
|
||||
}
|
||||
return;
|
||||
|
||||
case cJSON_String:
|
||||
if (strcmp(from->valuestring, to->valuestring) != 0)
|
||||
{
|
||||
cJSONUtils_GeneratePatch(patches, "replace", path, 0, to);
|
||||
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
|
||||
}
|
||||
return;
|
||||
|
||||
case cJSON_Array:
|
||||
{
|
||||
int c = 0;
|
||||
char *newpath = (char*)malloc(strlen(path) + 23); /* Allow space for 64bit int. */
|
||||
size_t c = 0;
|
||||
unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + 23); /* Allow space for 64bit int. */
|
||||
/* generate patches for all array elements that exist in "from" and "to" */
|
||||
for (c = 0, from = from->child, to = to->child; from && to; from = from->next, to = to->next, c++)
|
||||
for ((void)(c = 0), (void)(from = from->child), to = to->child; from && to; (void)(from = from->next), (void)(to = to->next), c++)
|
||||
{
|
||||
sprintf(newpath, "%s/%d", path, c); /* path of the current array element */
|
||||
/* check if conversion to unsigned long is valid
|
||||
* This should be eliminated at compile time by dead code elimination
|
||||
* if size_t is an alias of unsigned long, or if it is bigger */
|
||||
if (c > ULONG_MAX)
|
||||
{
|
||||
free(newpath);
|
||||
return;
|
||||
}
|
||||
sprintf((char*)newpath, "%s/%lu", path, (unsigned long)c); /* path of the current array element */
|
||||
cJSONUtils_CompareToPatch(patches, newpath, from, to);
|
||||
}
|
||||
/* remove leftover elements from 'from' that are not in 'to' */
|
||||
for (; from; from = from->next, c++)
|
||||
for (; from; (void)(from = from->next), c++)
|
||||
{
|
||||
sprintf(newpath, "%d", c);
|
||||
cJSONUtils_GeneratePatch(patches, "remove", path, newpath, 0);
|
||||
/* check if conversion to unsigned long is valid
|
||||
* This should be eliminated at compile time by dead code elimination
|
||||
* if size_t is an alias of unsigned long, or if it is bigger */
|
||||
if (c > ULONG_MAX)
|
||||
{
|
||||
free(newpath);
|
||||
return;
|
||||
}
|
||||
sprintf((char*)newpath, "%lu", (unsigned long)c);
|
||||
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, newpath, 0);
|
||||
}
|
||||
/* add new elements in 'to' that were not in 'from' */
|
||||
for (; to; to = to->next, c++)
|
||||
for (; to; (void)(to = to->next), c++)
|
||||
{
|
||||
cJSONUtils_GeneratePatch(patches, "add", path, "-", to);
|
||||
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to);
|
||||
}
|
||||
free(newpath);
|
||||
return;
|
||||
@@ -597,12 +639,12 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *f
|
||||
/* for all object values in the object with more of them */
|
||||
while (a || b)
|
||||
{
|
||||
int diff = (!a) ? 1 : ((!b) ? -1 : cJSONUtils_strcasecmp(a->string, b->string));
|
||||
int diff = (!a) ? 1 : ((!b) ? -1 : cJSONUtils_strcasecmp((unsigned char*)a->string, (unsigned char*)b->string));
|
||||
if (!diff)
|
||||
{
|
||||
/* both object keys are the same */
|
||||
char *newpath = (char*)malloc(strlen(path) + cJSONUtils_PointerEncodedstrlen(a->string) + 2);
|
||||
cJSONUtils_PointerEncodedstrcpy(newpath + sprintf(newpath, "%s/", path), a->string);
|
||||
unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + cJSONUtils_PointerEncodedstrlen((unsigned char*)a->string) + 2);
|
||||
cJSONUtils_PointerEncodedstrcpy(newpath + sprintf((char*)newpath, "%s/", path), (unsigned char*)a->string);
|
||||
/* create a patch for the element */
|
||||
cJSONUtils_CompareToPatch(patches, newpath, a, b);
|
||||
free(newpath);
|
||||
@@ -612,13 +654,13 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *f
|
||||
else if (diff < 0)
|
||||
{
|
||||
/* object element doesn't exist in 'to' --> remove it */
|
||||
cJSONUtils_GeneratePatch(patches, "remove", path, a->string, 0);
|
||||
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, (unsigned char*)a->string, 0);
|
||||
a = a->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* object element doesn't exist in 'from' --> add it */
|
||||
cJSONUtils_GeneratePatch(patches, "add", path, b->string, b);
|
||||
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (unsigned char*)b->string, b);
|
||||
b = b->next;
|
||||
}
|
||||
}
|
||||
@@ -630,10 +672,10 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *f
|
||||
}
|
||||
}
|
||||
|
||||
cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to)
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON *from, cJSON *to)
|
||||
{
|
||||
cJSON *patches = cJSON_CreateArray();
|
||||
cJSONUtils_CompareToPatch(patches, "", from, to);
|
||||
cJSONUtils_CompareToPatch(patches, (const unsigned char*)"", from, to);
|
||||
|
||||
return patches;
|
||||
}
|
||||
@@ -651,7 +693,7 @@ static cJSON *cJSONUtils_SortList(cJSON *list)
|
||||
return list;
|
||||
}
|
||||
|
||||
while (ptr && ptr->next && (cJSONUtils_strcasecmp(ptr->string, ptr->next->string) < 0))
|
||||
while (ptr && ptr->next && (cJSONUtils_strcasecmp((unsigned char*)ptr->string, (unsigned char*)ptr->next->string) < 0))
|
||||
{
|
||||
/* Test for list sorted. */
|
||||
ptr = ptr->next;
|
||||
@@ -688,7 +730,7 @@ static cJSON *cJSONUtils_SortList(cJSON *list)
|
||||
|
||||
while (first && second) /* Merge the sub-lists */
|
||||
{
|
||||
if (cJSONUtils_strcasecmp(first->string, second->string) < 0)
|
||||
if (cJSONUtils_strcasecmp((unsigned char*)first->string, (unsigned char*)second->string) < 0)
|
||||
{
|
||||
if (!list)
|
||||
{
|
||||
@@ -745,21 +787,21 @@ static cJSON *cJSONUtils_SortList(cJSON *list)
|
||||
return list;
|
||||
}
|
||||
|
||||
void cJSONUtils_SortObject(cJSON *object)
|
||||
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON *object)
|
||||
{
|
||||
object->child = cJSONUtils_SortList(object->child);
|
||||
}
|
||||
|
||||
cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
|
||||
{
|
||||
if (!patch || ((patch->type & 0xFF) != cJSON_Object))
|
||||
if (!cJSON_IsObject(patch))
|
||||
{
|
||||
/* scalar value, array or NULL, just duplicate */
|
||||
cJSON_Delete(target);
|
||||
return cJSON_Duplicate(patch, 1);
|
||||
}
|
||||
|
||||
if (!target || ((target->type & 0xFF) != cJSON_Object))
|
||||
if (!cJSON_IsObject(target))
|
||||
{
|
||||
cJSON_Delete(target);
|
||||
target = cJSON_CreateObject();
|
||||
@@ -768,7 +810,7 @@ cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
|
||||
patch = patch->child;
|
||||
while (patch)
|
||||
{
|
||||
if ((patch->type & 0xFF) == cJSON_NULL)
|
||||
if (cJSON_IsNull(patch))
|
||||
{
|
||||
/* NULL is the indicator to remove a value, see RFC7396 */
|
||||
cJSON_DeleteItemFromObject(target, patch->string);
|
||||
@@ -783,7 +825,7 @@ cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
|
||||
return target;
|
||||
}
|
||||
|
||||
cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
|
||||
{
|
||||
cJSON *patch = NULL;
|
||||
if (!to)
|
||||
@@ -791,7 +833,7 @@ cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
|
||||
/* patch to delete everything */
|
||||
return cJSON_CreateNull();
|
||||
}
|
||||
if (((to->type & 0xFF) != cJSON_Object) || !from || ((from->type & 0xFF) != cJSON_Object))
|
||||
if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
|
||||
{
|
||||
return cJSON_Duplicate(to, 1);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#include "cJSON.h"
|
||||
|
||||
/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
|
||||
cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer);
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer);
|
||||
|
||||
/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
|
||||
cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to);
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON *from, cJSON *to);
|
||||
/* Utility for generating patch array entries. */
|
||||
void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val);
|
||||
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val);
|
||||
/* Returns 0 for success. */
|
||||
int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
|
||||
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
|
||||
|
||||
/*
|
||||
// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
|
||||
@@ -33,12 +33,12 @@ int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
|
||||
|
||||
/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
|
||||
/* target will be modified by patch. return value is new ptr for target. */
|
||||
cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch);
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch);
|
||||
/* generates a patch to move from -> to */
|
||||
cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to);
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to);
|
||||
|
||||
/* Given a root object and a target object, construct a pointer from one to the other. */
|
||||
char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target);
|
||||
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target);
|
||||
|
||||
/* Sorts the members of the object into alphabetical order. */
|
||||
void cJSONUtils_SortObject(cJSON *object);
|
||||
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON *object);
|
||||
|
||||
1
fuzzing/.gitignore
vendored
Normal file
1
fuzzing/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
afl-build
|
||||
28
fuzzing/CMakeLists.txt
Normal file
28
fuzzing/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
option(ENABLE_FUZZING "Create executables and targets for fuzzing cJSON with afl." Off)
|
||||
if (ENABLE_FUZZING)
|
||||
find_program(AFL_FUZZ afl-fuzz)
|
||||
if ("${AFL_FUZZ}" MATCHES "AFL_FUZZ-NOTFOUND")
|
||||
message(FATAL_ERROR "Couldn't find afl-fuzz.")
|
||||
endif()
|
||||
|
||||
add_executable(afl-main afl.c)
|
||||
target_link_libraries(afl-main "${CJSON_LIB}")
|
||||
|
||||
if (NOT ENABLE_SANITIZERS)
|
||||
message(FATAL_ERROR "Enable sanitizers with -DENABLE_SANITIZERS=On to do fuzzing.")
|
||||
endif()
|
||||
|
||||
option(ENABLE_FUZZING_PRINT "Fuzz printing functions together with parser." On)
|
||||
set(fuzz_print_parameter "no")
|
||||
if (ENABLE_FUZZING_PRINT)
|
||||
set(fuzz_print_parameter "yes")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error")
|
||||
|
||||
add_custom_target(afl
|
||||
COMMAND "${AFL_FUZZ}" -i "${CMAKE_CURRENT_SOURCE_DIR}/inputs" -o "${CMAKE_CURRENT_BINARY_DIR}/findings" -x "${CMAKE_CURRENT_SOURCE_DIR}/json.dict" -- "${CMAKE_CURRENT_BINARY_DIR}/afl-main" "@@" "${fuzz_print_parameter}"
|
||||
DEPENDS afl-main)
|
||||
|
||||
|
||||
endif()
|
||||
5
fuzzing/afl-prepare-linux.sh
Executable file
5
fuzzing/afl-prepare-linux.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
echo core | sudo tee /proc/sys/kernel/core_pattern
|
||||
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
||||
176
fuzzing/afl.c
Normal file
176
fuzzing/afl.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../cJSON.h"
|
||||
|
||||
static char *read_file(const char *filename)
|
||||
{
|
||||
FILE *file = NULL;
|
||||
long length = 0;
|
||||
char *content = NULL;
|
||||
size_t read_chars = 0;
|
||||
|
||||
/* open in read binary mode */
|
||||
file = fopen(filename, "rb");
|
||||
if (file == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* get the length */
|
||||
if (fseek(file, 0, SEEK_END) != 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
length = ftell(file);
|
||||
if (length < 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
if (fseek(file, 0, SEEK_SET) != 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* allocate content buffer */
|
||||
content = (char*)malloc((size_t)length + sizeof('\0'));
|
||||
if (content == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* read the file into memory */
|
||||
read_chars = fread(content, sizeof(char), (size_t)length, file);
|
||||
if ((long)read_chars != length)
|
||||
{
|
||||
free(content);
|
||||
content = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
content[read_chars] = '\0';
|
||||
|
||||
|
||||
cleanup:
|
||||
if (file != NULL)
|
||||
{
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char *filename = NULL;
|
||||
cJSON *item = NULL;
|
||||
char *json = NULL;
|
||||
int status = EXIT_FAILURE;
|
||||
char *printed_json = NULL;
|
||||
|
||||
if ((argc < 2) || (argc > 3))
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf("%s input_file [enable_printing]\n", argv[0]);
|
||||
printf("\t input_file: file containing the test data\n");
|
||||
printf("\t enable_printing: print after parsing, 'yes' or 'no', defaults to 'no'\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
filename = argv[1];
|
||||
|
||||
#if __AFL_HAVE_MANUAL_CONTROL
|
||||
while (__AFL_LOOP(1000))
|
||||
{
|
||||
#endif
|
||||
status = EXIT_SUCCESS;
|
||||
|
||||
json = read_file(filename);
|
||||
if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0'))
|
||||
{
|
||||
status = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
item = cJSON_Parse(json + 2);
|
||||
if (item == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((argc == 3) && (strncmp(argv[2], "yes", 3) == 0))
|
||||
{
|
||||
int do_format = 0;
|
||||
if (json[1] == 'f')
|
||||
{
|
||||
do_format = 1;
|
||||
}
|
||||
|
||||
if (json[0] == 'b')
|
||||
{
|
||||
/* buffered printing */
|
||||
printed_json = cJSON_PrintBuffered(item, 1, do_format);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* unbuffered printing */
|
||||
if (do_format)
|
||||
{
|
||||
printed_json = cJSON_Print(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
printed_json = cJSON_PrintUnformatted(item);
|
||||
}
|
||||
}
|
||||
if (printed_json == NULL)
|
||||
{
|
||||
status = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
printf("%s\n", printed_json);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (item != NULL)
|
||||
{
|
||||
cJSON_Delete(item);
|
||||
item = NULL;
|
||||
}
|
||||
if (json != NULL)
|
||||
{
|
||||
free(json);
|
||||
json = NULL;
|
||||
}
|
||||
if (printed_json != NULL)
|
||||
{
|
||||
free(printed_json);
|
||||
printed_json = NULL;
|
||||
}
|
||||
#if __AFL_HAVE_MANUAL_CONTROL
|
||||
}
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
9
fuzzing/afl.sh
Executable file
9
fuzzing/afl.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkdir -p afl-build || exit 1
|
||||
cd afl-build || exit 1
|
||||
#cleanup
|
||||
rm -r -- *
|
||||
|
||||
CC=afl-clang-fast cmake ../.. -DENABLE_FUZZING=On -DENABLE_SANITIZERS=On -DBUILD_SHARED_LIBS=Off
|
||||
make afl
|
||||
22
fuzzing/inputs/test1
Normal file
22
fuzzing/inputs/test1
Normal file
@@ -0,0 +1,22 @@
|
||||
bf{
|
||||
"glossary": {
|
||||
"title": "example glossary",
|
||||
"GlossDiv": {
|
||||
"title": "S",
|
||||
"GlossList": {
|
||||
"GlossEntry": {
|
||||
"ID": "SGML",
|
||||
"SortAs": "SGML",
|
||||
"GlossTerm": "Standard Generalized Markup Language",
|
||||
"Acronym": "SGML",
|
||||
"Abbrev": "ISO 8879:1986",
|
||||
"GlossDef": {
|
||||
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||
"GlossSeeAlso": ["GML", "XML"]
|
||||
},
|
||||
"GlossSee": "markup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
fuzzing/inputs/test10
Normal file
1
fuzzing/inputs/test10
Normal file
@@ -0,0 +1 @@
|
||||
bf["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
8
fuzzing/inputs/test11
Normal file
8
fuzzing/inputs/test11
Normal file
@@ -0,0 +1,8 @@
|
||||
bf{
|
||||
"name": "Jack (\"Bee\") Nimble",
|
||||
"format": {"type": "rect",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"interlace": false,"frame rate": 24
|
||||
}
|
||||
}
|
||||
11
fuzzing/inputs/test2
Normal file
11
fuzzing/inputs/test2
Normal file
@@ -0,0 +1,11 @@
|
||||
bf{"menu": {
|
||||
"id": "file",
|
||||
"value": "File",
|
||||
"popup": {
|
||||
"menuitem": [
|
||||
{"value": "New", "onclick": "CreateNewDoc()"},
|
||||
{"value": "Open", "onclick": "OpenDoc()"},
|
||||
{"value": "Close", "onclick": "CloseDoc()"}
|
||||
]
|
||||
}
|
||||
}}
|
||||
26
fuzzing/inputs/test3
Normal file
26
fuzzing/inputs/test3
Normal file
@@ -0,0 +1,26 @@
|
||||
bf{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
||||
26
fuzzing/inputs/test3.bu
Normal file
26
fuzzing/inputs/test3.bu
Normal file
@@ -0,0 +1,26 @@
|
||||
bu{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
||||
26
fuzzing/inputs/test3.uf
Normal file
26
fuzzing/inputs/test3.uf
Normal file
@@ -0,0 +1,26 @@
|
||||
uf{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
||||
26
fuzzing/inputs/test3.uu
Normal file
26
fuzzing/inputs/test3.uu
Normal file
@@ -0,0 +1,26 @@
|
||||
uu{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
||||
88
fuzzing/inputs/test4
Normal file
88
fuzzing/inputs/test4
Normal file
@@ -0,0 +1,88 @@
|
||||
bf{"web-app": {
|
||||
"servlet": [
|
||||
{
|
||||
"servlet-name": "cofaxCDS",
|
||||
"servlet-class": "org.cofax.cds.CDSServlet",
|
||||
"init-param": {
|
||||
"configGlossary:installationAt": "Philadelphia, PA",
|
||||
"configGlossary:adminEmail": "ksm@pobox.com",
|
||||
"configGlossary:poweredBy": "Cofax",
|
||||
"configGlossary:poweredByIcon": "/images/cofax.gif",
|
||||
"configGlossary:staticPath": "/content/static",
|
||||
"templateProcessorClass": "org.cofax.WysiwygTemplate",
|
||||
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
|
||||
"templatePath": "templates",
|
||||
"templateOverridePath": "",
|
||||
"defaultListTemplate": "listTemplate.htm",
|
||||
"defaultFileTemplate": "articleTemplate.htm",
|
||||
"useJSP": false,
|
||||
"jspListTemplate": "listTemplate.jsp",
|
||||
"jspFileTemplate": "articleTemplate.jsp",
|
||||
"cachePackageTagsTrack": 200,
|
||||
"cachePackageTagsStore": 200,
|
||||
"cachePackageTagsRefresh": 60,
|
||||
"cacheTemplatesTrack": 100,
|
||||
"cacheTemplatesStore": 50,
|
||||
"cacheTemplatesRefresh": 15,
|
||||
"cachePagesTrack": 200,
|
||||
"cachePagesStore": 100,
|
||||
"cachePagesRefresh": 10,
|
||||
"cachePagesDirtyRead": 10,
|
||||
"searchEngineListTemplate": "forSearchEnginesList.htm",
|
||||
"searchEngineFileTemplate": "forSearchEngines.htm",
|
||||
"searchEngineRobotsDb": "WEB-INF/robots.db",
|
||||
"useDataStore": true,
|
||||
"dataStoreClass": "org.cofax.SqlDataStore",
|
||||
"redirectionClass": "org.cofax.SqlRedirection",
|
||||
"dataStoreName": "cofax",
|
||||
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
|
||||
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
|
||||
"dataStoreUser": "sa",
|
||||
"dataStorePassword": "dataStoreTestQuery",
|
||||
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
|
||||
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
|
||||
"dataStoreInitConns": 10,
|
||||
"dataStoreMaxConns": 100,
|
||||
"dataStoreConnUsageLimit": 100,
|
||||
"dataStoreLogLevel": "debug",
|
||||
"maxUrlLength": 500}},
|
||||
{
|
||||
"servlet-name": "cofaxEmail",
|
||||
"servlet-class": "org.cofax.cds.EmailServlet",
|
||||
"init-param": {
|
||||
"mailHost": "mail1",
|
||||
"mailHostOverride": "mail2"}},
|
||||
{
|
||||
"servlet-name": "cofaxAdmin",
|
||||
"servlet-class": "org.cofax.cds.AdminServlet"},
|
||||
|
||||
{
|
||||
"servlet-name": "fileServlet",
|
||||
"servlet-class": "org.cofax.cds.FileServlet"},
|
||||
{
|
||||
"servlet-name": "cofaxTools",
|
||||
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
|
||||
"init-param": {
|
||||
"templatePath": "toolstemplates/",
|
||||
"log": 1,
|
||||
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
|
||||
"logMaxSize": "",
|
||||
"dataLog": 1,
|
||||
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
|
||||
"dataLogMaxSize": "",
|
||||
"removePageCache": "/content/admin/remove?cache=pages&id=",
|
||||
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
|
||||
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
|
||||
"lookInContext": 1,
|
||||
"adminGroupID": 4,
|
||||
"betaServer": true}}],
|
||||
"servlet-mapping": {
|
||||
"cofaxCDS": "/",
|
||||
"cofaxEmail": "/cofaxutil/aemail/*",
|
||||
"cofaxAdmin": "/admin/*",
|
||||
"fileServlet": "/static/*",
|
||||
"cofaxTools": "/tools/*"},
|
||||
|
||||
"taglib": {
|
||||
"taglib-uri": "cofax.tld",
|
||||
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
|
||||
27
fuzzing/inputs/test5
Normal file
27
fuzzing/inputs/test5
Normal file
@@ -0,0 +1,27 @@
|
||||
bf{"menu": {
|
||||
"header": "SVG Viewer",
|
||||
"items": [
|
||||
{"id": "Open"},
|
||||
{"id": "OpenNew", "label": "Open New"},
|
||||
null,
|
||||
{"id": "ZoomIn", "label": "Zoom In"},
|
||||
{"id": "ZoomOut", "label": "Zoom Out"},
|
||||
{"id": "OriginalView", "label": "Original View"},
|
||||
null,
|
||||
{"id": "Quality"},
|
||||
{"id": "Pause"},
|
||||
{"id": "Mute"},
|
||||
null,
|
||||
{"id": "Find", "label": "Find..."},
|
||||
{"id": "FindAgain", "label": "Find Again"},
|
||||
{"id": "Copy"},
|
||||
{"id": "CopyAgain", "label": "Copy Again"},
|
||||
{"id": "CopySVG", "label": "Copy SVG"},
|
||||
{"id": "ViewSVG", "label": "View SVG"},
|
||||
{"id": "ViewSource", "label": "View Source"},
|
||||
{"id": "SaveAs", "label": "Save As"},
|
||||
null,
|
||||
{"id": "Help"},
|
||||
{"id": "About", "label": "About Adobe CVG Viewer..."}
|
||||
]
|
||||
}}
|
||||
16
fuzzing/inputs/test6
Normal file
16
fuzzing/inputs/test6
Normal file
@@ -0,0 +1,16 @@
|
||||
bf<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
html, body, iframe { margin: 0; padding: 0; height: 100%; }
|
||||
iframe { display: block; width: 100%; border: none; }
|
||||
</style>
|
||||
<title>Application Error</title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="//s3.amazonaws.com/heroku_pages/error.html">
|
||||
<p>Application Error</p>
|
||||
</iframe>
|
||||
</body>
|
||||
</html>
|
||||
22
fuzzing/inputs/test7
Normal file
22
fuzzing/inputs/test7
Normal file
@@ -0,0 +1,22 @@
|
||||
bf[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]
|
||||
13
fuzzing/inputs/test8
Normal file
13
fuzzing/inputs/test8
Normal file
@@ -0,0 +1,13 @@
|
||||
bf{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http:/*www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
}
|
||||
}
|
||||
5
fuzzing/inputs/test9
Normal file
5
fuzzing/inputs/test9
Normal file
@@ -0,0 +1,5 @@
|
||||
bf[
|
||||
[0, -1, 0],
|
||||
[1, 0, 0],
|
||||
[0, 0, 1]
|
||||
]
|
||||
47
fuzzing/json.dict
Normal file
47
fuzzing/json.dict
Normal file
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# AFL dictionary for JSON
|
||||
# -----------------------------
|
||||
#
|
||||
|
||||
object_start="{"
|
||||
object_end="}"
|
||||
object_empty="{}"
|
||||
object_one_element="{\"one\":1}"
|
||||
object_two_elements="{\"1\":1,\"2\":2}"
|
||||
object_separator=":"
|
||||
|
||||
array_start="["
|
||||
array_end="]"
|
||||
array_empty="[]"
|
||||
array_one_element="[1]"
|
||||
array_two_elements="[1,2]"
|
||||
|
||||
separator=","
|
||||
|
||||
escape_sequence_b="\\b"
|
||||
escape_sequence_f="\\f"
|
||||
escape_sequence_n="\\n"
|
||||
escape_sequence_r="\\r"
|
||||
escape_sequence_t="\\t"
|
||||
escape_sequence_quote="\\\""
|
||||
escape_sequence_backslash="\\\\"
|
||||
escapce_sequence_slash="\\/"
|
||||
escpae_sequence_utf16_base="\\u"
|
||||
escape_sequence_utf16="\\u12ab"
|
||||
|
||||
number_integer="1"
|
||||
number_double="1.0"
|
||||
number_negative_integer="-1"
|
||||
number_negative_double="-1.0"
|
||||
number_engineering1="1e1"
|
||||
number_engineering2="1e-1"
|
||||
number_positive_integer="+1"
|
||||
number_positive_double="+1.0"
|
||||
number_e="e"
|
||||
number_plus="+"
|
||||
number_minus="-"
|
||||
number_separator="."
|
||||
|
||||
null="null"
|
||||
true="true"
|
||||
false="false"
|
||||
148
test.c
148
test.c
@@ -25,57 +25,6 @@
|
||||
#include <string.h>
|
||||
#include "cJSON.h"
|
||||
|
||||
/* Parse text to JSON, then render back to text, and print! */
|
||||
static void doit(char *text)
|
||||
{
|
||||
char *out = NULL;
|
||||
cJSON *json = NULL;
|
||||
|
||||
json = cJSON_Parse(text);
|
||||
if (!json)
|
||||
{
|
||||
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
|
||||
}
|
||||
else
|
||||
{
|
||||
out = cJSON_Print(json);
|
||||
cJSON_Delete(json);
|
||||
printf("%s\n", out);
|
||||
free(out);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Read a file, parse, render back, etc. */
|
||||
static void dofile(char *filename)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
long len = 0;
|
||||
char *data = NULL;
|
||||
|
||||
/* open in read binary mode */
|
||||
f = fopen(filename,"rb");
|
||||
/* get the length */
|
||||
fseek(f, 0, SEEK_END);
|
||||
len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
data = (char*)malloc(len + 1);
|
||||
if (data == NULL)
|
||||
{
|
||||
printf("Failed to allocate memory.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fread(data, 1, len, f);
|
||||
data[len] = '\0';
|
||||
fclose(f);
|
||||
|
||||
doit(data);
|
||||
free(data);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Used by some code below as an example datatype. */
|
||||
struct record
|
||||
{
|
||||
@@ -97,8 +46,8 @@ static int print_preallocated(cJSON *root)
|
||||
char *out = NULL;
|
||||
char *buf = NULL;
|
||||
char *buf_fail = NULL;
|
||||
int len = 0;
|
||||
int len_fail = 0;
|
||||
size_t len = 0;
|
||||
size_t len_fail = 0;
|
||||
|
||||
/* formatted print */
|
||||
out = cJSON_Print(root);
|
||||
@@ -123,7 +72,7 @@ static int print_preallocated(cJSON *root)
|
||||
}
|
||||
|
||||
/* Print to buffer */
|
||||
if (!cJSON_PrintPreallocated(root, buf, len, 1)) {
|
||||
if (!cJSON_PrintPreallocated(root, buf, (int)len, 1)) {
|
||||
printf("cJSON_PrintPreallocated failed!\n");
|
||||
if (strcmp(out, buf) != 0) {
|
||||
printf("cJSON_PrintPreallocated not the same as cJSON_Print!\n");
|
||||
@@ -140,7 +89,7 @@ static int print_preallocated(cJSON *root)
|
||||
printf("%s\n", buf);
|
||||
|
||||
/* force it to fail */
|
||||
if (cJSON_PrintPreallocated(root, buf_fail, len_fail, 1)) {
|
||||
if (cJSON_PrintPreallocated(root, buf_fail, (int)len_fail, 1)) {
|
||||
printf("cJSON_PrintPreallocated failed to show error with insufficient memory!\n");
|
||||
printf("cJSON_Print result:\n%s\n", out);
|
||||
printf("cJSON_PrintPreallocated result:\n%s\n", buf_fail);
|
||||
@@ -309,98 +258,9 @@ static void create_objects(void)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* a bunch of json: */
|
||||
char text1[] =
|
||||
"{\n"
|
||||
"\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n"
|
||||
"\"format\": {\"type\": \"rect\", \n"
|
||||
"\"width\": 1920, \n"
|
||||
"\"height\": 1080, \n"
|
||||
"\"interlace\": false,\"frame rate\": 24\n"
|
||||
"}\n"
|
||||
"}";
|
||||
char text2[] = "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";
|
||||
char text3[] =
|
||||
"[\n"
|
||||
" [0, -1, 0],\n"
|
||||
" [1, 0, 0],\n"
|
||||
" [0, 0, 1]\n"
|
||||
"\t]\n";
|
||||
char text4[] =
|
||||
"{\n"
|
||||
"\t\t\"Image\": {\n"
|
||||
"\t\t\t\"Width\": 800,\n"
|
||||
"\t\t\t\"Height\": 600,\n"
|
||||
"\t\t\t\"Title\": \"View from 15th Floor\",\n"
|
||||
"\t\t\t\"Thumbnail\": {\n"
|
||||
"\t\t\t\t\"Url\": \"http:/*www.example.com/image/481989943\",\n"
|
||||
"\t\t\t\t\"Height\": 125,\n"
|
||||
"\t\t\t\t\"Width\": \"100\"\n"
|
||||
"\t\t\t},\n"
|
||||
"\t\t\t\"IDs\": [116, 943, 234, 38793]\n"
|
||||
"\t\t}\n"
|
||||
"\t}";
|
||||
char text5[] =
|
||||
"[\n"
|
||||
"\t {\n"
|
||||
"\t \"precision\": \"zip\",\n"
|
||||
"\t \"Latitude\": 37.7668,\n"
|
||||
"\t \"Longitude\": -122.3959,\n"
|
||||
"\t \"Address\": \"\",\n"
|
||||
"\t \"City\": \"SAN FRANCISCO\",\n"
|
||||
"\t \"State\": \"CA\",\n"
|
||||
"\t \"Zip\": \"94107\",\n"
|
||||
"\t \"Country\": \"US\"\n"
|
||||
"\t },\n"
|
||||
"\t {\n"
|
||||
"\t \"precision\": \"zip\",\n"
|
||||
"\t \"Latitude\": 37.371991,\n"
|
||||
"\t \"Longitude\": -122.026020,\n"
|
||||
"\t \"Address\": \"\",\n"
|
||||
"\t \"City\": \"SUNNYVALE\",\n"
|
||||
"\t \"State\": \"CA\",\n"
|
||||
"\t \"Zip\": \"94085\",\n"
|
||||
"\t \"Country\": \"US\"\n"
|
||||
"\t }\n"
|
||||
"\t ]";
|
||||
|
||||
char text6[] =
|
||||
"<!DOCTYPE html>"
|
||||
"<html>\n"
|
||||
"<head>\n"
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
|
||||
" <style type=\"text/css\">\n"
|
||||
" html, body, iframe { margin: 0; padding: 0; height: 100%; }\n"
|
||||
" iframe { display: block; width: 100%; border: none; }\n"
|
||||
" </style>\n"
|
||||
"<title>Application Error</title>\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
" <iframe src=\"//s3.amazonaws.com/heroku_pages/error.html\">\n"
|
||||
" <p>Application Error</p>\n"
|
||||
" </iframe>\n"
|
||||
"</body>\n"
|
||||
"</html>\n";
|
||||
|
||||
/* print the version */
|
||||
printf("Version: %s\n", cJSON_Version());
|
||||
|
||||
/* Process each json textblock by parsing, then rebuilding: */
|
||||
doit(text1);
|
||||
doit(text2);
|
||||
doit(text3);
|
||||
doit(text4);
|
||||
doit(text5);
|
||||
doit(text6);
|
||||
|
||||
/* Parse standard testfiles: */
|
||||
/* dofile("../../tests/test1"); */
|
||||
/* dofile("../../tests/test2"); */
|
||||
/* dofile("../../tests/test3"); */
|
||||
/* dofile("../../tests/test4"); */
|
||||
/* dofile("../../tests/test5"); */
|
||||
/* dofile("../../tests/test6"); */
|
||||
|
||||
/* Now some samplecode for building objects concisely: */
|
||||
create_objects();
|
||||
|
||||
|
||||
65
tests/CMakeLists.txt
Normal file
65
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,65 @@
|
||||
if(ENABLE_CJSON_TEST)
|
||||
add_library(unity unity/src/unity.c)
|
||||
|
||||
# Disable -Werror for Unity
|
||||
list(FIND custom_compiler_flags "-Werror" werror_found)
|
||||
if (werror_found)
|
||||
target_compile_options(unity PRIVATE "-Wno-error")
|
||||
endif()
|
||||
# Disable -fvisibility=hidden for Unity
|
||||
list(FIND custom_compiler_flags "-fvisibility=hidden" visibility_found)
|
||||
if (visibility_found)
|
||||
target_compile_options(unity PRIVATE "-fvisibility=default")
|
||||
endif()
|
||||
|
||||
#copy test files
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs")
|
||||
file(GLOB test_files "inputs/*")
|
||||
file(COPY ${test_files} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/inputs/")
|
||||
|
||||
set(unity_tests
|
||||
parse_examples
|
||||
parse_number
|
||||
parse_hex4
|
||||
parse_string
|
||||
parse_array
|
||||
parse_object
|
||||
parse_value
|
||||
print_string
|
||||
print_number
|
||||
print_array
|
||||
print_object
|
||||
print_value
|
||||
misc_tests
|
||||
)
|
||||
|
||||
add_library(test-common common.c)
|
||||
|
||||
option(ENABLE_VALGRIND OFF "Enable the valgrind memory checker for the tests.")
|
||||
if (ENABLE_VALGRIND)
|
||||
find_program(MEMORYCHECK_COMMAND valgrind)
|
||||
if ("${MEMORYCHECK_COMMAND}" MATCHES "MEMORYCHECK_COMMAND-NOTFOUND")
|
||||
message(WARNING "Valgrind couldn't be found.")
|
||||
unset(MEMORYCHECK_COMMAND)
|
||||
else()
|
||||
set(MEMORYCHECK_COMMAND_OPTIONS --trace-children=yes --leak-check=full --error-exitcode=1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#"check" target that automatically builds everything and runs the tests
|
||||
add_custom_target(check
|
||||
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
|
||||
DEPENDS ${unity_tests})
|
||||
|
||||
foreach(unity_test ${unity_tests})
|
||||
add_executable("${unity_test}" "${unity_test}.c")
|
||||
target_link_libraries("${unity_test}" "${CJSON_LIB}" unity test-common)
|
||||
if(MEMORYCHECK_COMMAND)
|
||||
add_test(NAME "${unity_test}"
|
||||
COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} "./${unity_test}")
|
||||
else()
|
||||
add_test(NAME "${unity_test}"
|
||||
COMMAND "./${unity_test}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
97
tests/common.c
Normal file
97
tests/common.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
CJSON_PUBLIC(void) reset(cJSON *item)
|
||||
{
|
||||
if ((item != NULL) && (item->child != NULL))
|
||||
{
|
||||
cJSON_Delete(item->child);
|
||||
}
|
||||
if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference))
|
||||
{
|
||||
global_hooks.deallocate(item->valuestring);
|
||||
}
|
||||
if ((item->string != NULL) && !(item->type & cJSON_StringIsConst))
|
||||
{
|
||||
global_hooks.deallocate(item->string);
|
||||
}
|
||||
|
||||
memset(item, 0, sizeof(cJSON));
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(char*) read_file(const char *filename)
|
||||
{
|
||||
FILE *file = NULL;
|
||||
long length = 0;
|
||||
char *content = NULL;
|
||||
size_t read_chars = 0;
|
||||
|
||||
/* open in read binary mode */
|
||||
file = fopen(filename, "rb");
|
||||
if (file == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* get the length */
|
||||
if (fseek(file, 0, SEEK_END) != 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
length = ftell(file);
|
||||
if (length < 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
if (fseek(file, 0, SEEK_SET) != 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* allocate content buffer */
|
||||
content = (char*)malloc((size_t)length + sizeof('\0'));
|
||||
if (content == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* read the file into memory */
|
||||
read_chars = fread(content, sizeof(char), (size_t)length, file);
|
||||
if ((long)read_chars != length)
|
||||
{
|
||||
free(content);
|
||||
content = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
content[read_chars] = '\0';
|
||||
|
||||
|
||||
cleanup:
|
||||
if (file != NULL)
|
||||
{
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
51
tests/common.h
Normal file
51
tests/common.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CJSON_TESTS_COMMON_H
|
||||
#define CJSON_TESTS_COMMON_H
|
||||
|
||||
#include "../cJSON.c"
|
||||
|
||||
CJSON_PUBLIC(void) reset(cJSON *item);
|
||||
CJSON_PUBLIC(char*) read_file(const char *filename);
|
||||
|
||||
/* assertion helper macros */
|
||||
#define assert_has_type(item, item_type) TEST_ASSERT_BITS_MESSAGE(0xFF, item_type, item->type, "Item doesn't have expected type.")
|
||||
#define assert_has_no_reference(item) TEST_ASSERT_BITS_MESSAGE(cJSON_IsReference, 0, item->type, "Item should not have a string as reference.")
|
||||
#define assert_has_no_const_string(item) TEST_ASSERT_BITS_MESSAGE(cJSON_StringIsConst, 0, item->type, "Item should not have a const string.")
|
||||
#define assert_has_valuestring(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->valuestring, "Valuestring is NULL.")
|
||||
#define assert_has_no_valuestring(item) TEST_ASSERT_NULL_MESSAGE(item->valuestring, "Valuestring is not NULL.")
|
||||
#define assert_has_string(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->string, "String is NULL")
|
||||
#define assert_has_no_string(item) TEST_ASSERT_NULL_MESSAGE(item->string, "String is not NULL.")
|
||||
#define assert_not_in_list(item) \
|
||||
TEST_ASSERT_NULL_MESSAGE(item->next, "Linked list next pointer is not NULL.");\
|
||||
TEST_ASSERT_NULL_MESSAGE(item->prev, "Linked list previous pointer is not NULL.")
|
||||
#define assert_has_child(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->child, "Item doesn't have a child.")
|
||||
#define assert_has_no_child(item) TEST_ASSERT_NULL_MESSAGE(item->child, "Item has a child.")
|
||||
#define assert_is_invalid(item) \
|
||||
assert_has_type(item, cJSON_Invalid);\
|
||||
assert_not_in_list(item);\
|
||||
assert_has_no_child(item);\
|
||||
assert_has_no_string(item);\
|
||||
assert_has_no_valuestring(item)
|
||||
|
||||
#endif
|
||||
22
tests/inputs/test1.expected
Normal file
22
tests/inputs/test1.expected
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"glossary": {
|
||||
"title": "example glossary",
|
||||
"GlossDiv": {
|
||||
"title": "S",
|
||||
"GlossList": {
|
||||
"GlossEntry": {
|
||||
"ID": "SGML",
|
||||
"SortAs": "SGML",
|
||||
"GlossTerm": "Standard Generalized Markup Language",
|
||||
"Acronym": "SGML",
|
||||
"Abbrev": "ISO 8879:1986",
|
||||
"GlossDef": {
|
||||
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||
"GlossSeeAlso": ["GML", "XML"]
|
||||
},
|
||||
"GlossSee": "markup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
tests/inputs/test10
Normal file
1
tests/inputs/test10
Normal file
@@ -0,0 +1 @@
|
||||
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
1
tests/inputs/test10.expected
Normal file
1
tests/inputs/test10.expected
Normal file
@@ -0,0 +1 @@
|
||||
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
8
tests/inputs/test11
Normal file
8
tests/inputs/test11
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Jack (\"Bee\") Nimble",
|
||||
"format": {"type": "rect",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"interlace": false,"frame rate": 24
|
||||
}
|
||||
}
|
||||
10
tests/inputs/test11.expected
Normal file
10
tests/inputs/test11.expected
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Jack (\"Bee\") Nimble",
|
||||
"format": {
|
||||
"type": "rect",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"interlace": false,
|
||||
"frame rate": 24
|
||||
}
|
||||
}
|
||||
18
tests/inputs/test2.expected
Normal file
18
tests/inputs/test2.expected
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"menu": {
|
||||
"id": "file",
|
||||
"value": "File",
|
||||
"popup": {
|
||||
"menuitem": [{
|
||||
"value": "New",
|
||||
"onclick": "CreateNewDoc()"
|
||||
}, {
|
||||
"value": "Open",
|
||||
"onclick": "OpenDoc()"
|
||||
}, {
|
||||
"value": "Close",
|
||||
"onclick": "CloseDoc()"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
28
tests/inputs/test3.expected
Normal file
28
tests/inputs/test3.expected
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}
|
||||
}
|
||||
94
tests/inputs/test4.expected
Normal file
94
tests/inputs/test4.expected
Normal file
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"web-app": {
|
||||
"servlet": [{
|
||||
"servlet-name": "cofaxCDS",
|
||||
"servlet-class": "org.cofax.cds.CDSServlet",
|
||||
"init-param": {
|
||||
"configGlossary:installationAt": "Philadelphia, PA",
|
||||
"configGlossary:adminEmail": "ksm@pobox.com",
|
||||
"configGlossary:poweredBy": "Cofax",
|
||||
"configGlossary:poweredByIcon": "/images/cofax.gif",
|
||||
"configGlossary:staticPath": "/content/static",
|
||||
"templateProcessorClass": "org.cofax.WysiwygTemplate",
|
||||
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
|
||||
"templatePath": "templates",
|
||||
"templateOverridePath": "",
|
||||
"defaultListTemplate": "listTemplate.htm",
|
||||
"defaultFileTemplate": "articleTemplate.htm",
|
||||
"useJSP": false,
|
||||
"jspListTemplate": "listTemplate.jsp",
|
||||
"jspFileTemplate": "articleTemplate.jsp",
|
||||
"cachePackageTagsTrack": 200,
|
||||
"cachePackageTagsStore": 200,
|
||||
"cachePackageTagsRefresh": 60,
|
||||
"cacheTemplatesTrack": 100,
|
||||
"cacheTemplatesStore": 50,
|
||||
"cacheTemplatesRefresh": 15,
|
||||
"cachePagesTrack": 200,
|
||||
"cachePagesStore": 100,
|
||||
"cachePagesRefresh": 10,
|
||||
"cachePagesDirtyRead": 10,
|
||||
"searchEngineListTemplate": "forSearchEnginesList.htm",
|
||||
"searchEngineFileTemplate": "forSearchEngines.htm",
|
||||
"searchEngineRobotsDb": "WEB-INF/robots.db",
|
||||
"useDataStore": true,
|
||||
"dataStoreClass": "org.cofax.SqlDataStore",
|
||||
"redirectionClass": "org.cofax.SqlRedirection",
|
||||
"dataStoreName": "cofax",
|
||||
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
|
||||
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
|
||||
"dataStoreUser": "sa",
|
||||
"dataStorePassword": "dataStoreTestQuery",
|
||||
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
|
||||
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
|
||||
"dataStoreInitConns": 10,
|
||||
"dataStoreMaxConns": 100,
|
||||
"dataStoreConnUsageLimit": 100,
|
||||
"dataStoreLogLevel": "debug",
|
||||
"maxUrlLength": 500
|
||||
}
|
||||
}, {
|
||||
"servlet-name": "cofaxEmail",
|
||||
"servlet-class": "org.cofax.cds.EmailServlet",
|
||||
"init-param": {
|
||||
"mailHost": "mail1",
|
||||
"mailHostOverride": "mail2"
|
||||
}
|
||||
}, {
|
||||
"servlet-name": "cofaxAdmin",
|
||||
"servlet-class": "org.cofax.cds.AdminServlet"
|
||||
}, {
|
||||
"servlet-name": "fileServlet",
|
||||
"servlet-class": "org.cofax.cds.FileServlet"
|
||||
}, {
|
||||
"servlet-name": "cofaxTools",
|
||||
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
|
||||
"init-param": {
|
||||
"templatePath": "toolstemplates/",
|
||||
"log": 1,
|
||||
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
|
||||
"logMaxSize": "",
|
||||
"dataLog": 1,
|
||||
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
|
||||
"dataLogMaxSize": "",
|
||||
"removePageCache": "/content/admin/remove?cache=pages&id=",
|
||||
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
|
||||
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
|
||||
"lookInContext": 1,
|
||||
"adminGroupID": 4,
|
||||
"betaServer": true
|
||||
}
|
||||
}],
|
||||
"servlet-mapping": {
|
||||
"cofaxCDS": "/",
|
||||
"cofaxEmail": "/cofaxutil/aemail/*",
|
||||
"cofaxAdmin": "/admin/*",
|
||||
"fileServlet": "/static/*",
|
||||
"cofaxTools": "/tools/*"
|
||||
},
|
||||
"taglib": {
|
||||
"taglib-uri": "cofax.tld",
|
||||
"taglib-location": "/WEB-INF/tlds/cofax.tld"
|
||||
}
|
||||
}
|
||||
}
|
||||
54
tests/inputs/test5.expected
Normal file
54
tests/inputs/test5.expected
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"menu": {
|
||||
"header": "SVG Viewer",
|
||||
"items": [{
|
||||
"id": "Open"
|
||||
}, {
|
||||
"id": "OpenNew",
|
||||
"label": "Open New"
|
||||
}, null, {
|
||||
"id": "ZoomIn",
|
||||
"label": "Zoom In"
|
||||
}, {
|
||||
"id": "ZoomOut",
|
||||
"label": "Zoom Out"
|
||||
}, {
|
||||
"id": "OriginalView",
|
||||
"label": "Original View"
|
||||
}, null, {
|
||||
"id": "Quality"
|
||||
}, {
|
||||
"id": "Pause"
|
||||
}, {
|
||||
"id": "Mute"
|
||||
}, null, {
|
||||
"id": "Find",
|
||||
"label": "Find..."
|
||||
}, {
|
||||
"id": "FindAgain",
|
||||
"label": "Find Again"
|
||||
}, {
|
||||
"id": "Copy"
|
||||
}, {
|
||||
"id": "CopyAgain",
|
||||
"label": "Copy Again"
|
||||
}, {
|
||||
"id": "CopySVG",
|
||||
"label": "Copy SVG"
|
||||
}, {
|
||||
"id": "ViewSVG",
|
||||
"label": "View SVG"
|
||||
}, {
|
||||
"id": "ViewSource",
|
||||
"label": "View Source"
|
||||
}, {
|
||||
"id": "SaveAs",
|
||||
"label": "Save As"
|
||||
}, null, {
|
||||
"id": "Help"
|
||||
}, {
|
||||
"id": "About",
|
||||
"label": "About Adobe CVG Viewer..."
|
||||
}]
|
||||
}
|
||||
}
|
||||
22
tests/inputs/test7
Normal file
22
tests/inputs/test7
Normal file
@@ -0,0 +1,22 @@
|
||||
[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]
|
||||
19
tests/inputs/test7.expected
Normal file
19
tests/inputs/test7.expected
Normal file
@@ -0,0 +1,19 @@
|
||||
[{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
}, {
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.02602,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}]
|
||||
13
tests/inputs/test8
Normal file
13
tests/inputs/test8
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http:/*www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
}
|
||||
}
|
||||
13
tests/inputs/test8.expected
Normal file
13
tests/inputs/test8.expected
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http:/*www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
}
|
||||
}
|
||||
5
tests/inputs/test9
Normal file
5
tests/inputs/test9
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
[0, -1, 0],
|
||||
[1, 0, 0],
|
||||
[0, 0, 1]
|
||||
]
|
||||
1
tests/inputs/test9.expected
Normal file
1
tests/inputs/test9.expected
Normal file
@@ -0,0 +1 @@
|
||||
[[0, -1, 0], [1, 0, 0], [0, 0, 1]]
|
||||
197
tests/misc_tests.c
Normal file
197
tests/misc_tests.c
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
static void cjson_array_foreach_should_loop_over_arrays(void)
|
||||
{
|
||||
cJSON array[1];
|
||||
cJSON elements[10];
|
||||
cJSON *element_pointer = NULL;
|
||||
size_t i = 0;
|
||||
|
||||
memset(array, 0, sizeof(array));
|
||||
memset(elements, 0, sizeof(elements));
|
||||
|
||||
/* create array */
|
||||
array[0].child = &elements[0];
|
||||
elements[0].prev = NULL;
|
||||
elements[9].next = NULL;
|
||||
for (i = 0; i < 9; i++)
|
||||
{
|
||||
elements[i].next = &elements[i + 1];
|
||||
elements[i + 1].prev = &elements[i];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
cJSON_ArrayForEach(element_pointer, array)
|
||||
{
|
||||
TEST_ASSERT_TRUE_MESSAGE(element_pointer == &elements[i], "Not iterating over array properly");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void cjson_array_foreach_should_not_dereference_null_pointer(void)
|
||||
{
|
||||
cJSON *array = NULL;
|
||||
cJSON *element = NULL;
|
||||
cJSON_ArrayForEach(element, array);
|
||||
}
|
||||
|
||||
static void cjson_get_object_item_should_get_object_items(void)
|
||||
{
|
||||
cJSON *item = NULL;
|
||||
cJSON *found = NULL;
|
||||
|
||||
item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}");
|
||||
|
||||
found = cJSON_GetObjectItem(NULL, "test");
|
||||
TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer.");
|
||||
|
||||
found = cJSON_GetObjectItem(item, NULL);
|
||||
TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string.");
|
||||
|
||||
|
||||
found = cJSON_GetObjectItem(item, "one");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
|
||||
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1);
|
||||
|
||||
found = cJSON_GetObjectItem(item, "tWo");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
|
||||
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2);
|
||||
|
||||
found = cJSON_GetObjectItem(item, "three");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item.");
|
||||
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3);
|
||||
|
||||
found = cJSON_GetObjectItem(item, "four");
|
||||
TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there.");
|
||||
|
||||
cJSON_Delete(item);
|
||||
}
|
||||
|
||||
static void cjson_get_object_item_case_sensitive_should_get_object_items(void)
|
||||
{
|
||||
cJSON *item = NULL;
|
||||
cJSON *found = NULL;
|
||||
|
||||
item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}");
|
||||
|
||||
found = cJSON_GetObjectItemCaseSensitive(NULL, "test");
|
||||
TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer.");
|
||||
|
||||
found = cJSON_GetObjectItemCaseSensitive(item, NULL);
|
||||
TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string.");
|
||||
|
||||
found = cJSON_GetObjectItemCaseSensitive(item, "one");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
|
||||
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1);
|
||||
|
||||
found = cJSON_GetObjectItemCaseSensitive(item, "Two");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
|
||||
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2);
|
||||
|
||||
found = cJSON_GetObjectItemCaseSensitive(item, "tHree");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item.");
|
||||
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3);
|
||||
|
||||
found = cJSON_GetObjectItemCaseSensitive(item, "One");
|
||||
TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there.");
|
||||
|
||||
cJSON_Delete(item);
|
||||
}
|
||||
|
||||
static void typecheck_functions_should_check_type(void)
|
||||
{
|
||||
cJSON invalid[1];
|
||||
cJSON item[1];
|
||||
invalid->type = cJSON_Invalid;
|
||||
invalid->type |= cJSON_StringIsConst;
|
||||
item->type = cJSON_False;
|
||||
item->type |= cJSON_StringIsConst;
|
||||
|
||||
TEST_ASSERT_FALSE(cJSON_IsInvalid(NULL));
|
||||
TEST_ASSERT_FALSE(cJSON_IsInvalid(item));
|
||||
TEST_ASSERT_TRUE(cJSON_IsInvalid(invalid));
|
||||
|
||||
item->type = cJSON_False | cJSON_StringIsConst;
|
||||
TEST_ASSERT_FALSE(cJSON_IsFalse(NULL));
|
||||
TEST_ASSERT_FALSE(cJSON_IsFalse(invalid));
|
||||
TEST_ASSERT_TRUE(cJSON_IsFalse(item));
|
||||
TEST_ASSERT_TRUE(cJSON_IsBool(item));
|
||||
|
||||
item->type = cJSON_True | cJSON_StringIsConst;
|
||||
TEST_ASSERT_FALSE(cJSON_IsTrue(NULL));
|
||||
TEST_ASSERT_FALSE(cJSON_IsTrue(invalid));
|
||||
TEST_ASSERT_TRUE(cJSON_IsTrue(item));
|
||||
TEST_ASSERT_TRUE(cJSON_IsBool(item));
|
||||
|
||||
item->type = cJSON_NULL | cJSON_StringIsConst;
|
||||
TEST_ASSERT_FALSE(cJSON_IsNull(NULL));
|
||||
TEST_ASSERT_FALSE(cJSON_IsNull(invalid));
|
||||
TEST_ASSERT_TRUE(cJSON_IsNull(item));
|
||||
|
||||
item->type = cJSON_Number | cJSON_StringIsConst;
|
||||
TEST_ASSERT_FALSE(cJSON_IsNumber(NULL));
|
||||
TEST_ASSERT_FALSE(cJSON_IsNumber(invalid));
|
||||
TEST_ASSERT_TRUE(cJSON_IsNumber(item));
|
||||
|
||||
item->type = cJSON_String | cJSON_StringIsConst;
|
||||
TEST_ASSERT_FALSE(cJSON_IsString(NULL));
|
||||
TEST_ASSERT_FALSE(cJSON_IsString(invalid));
|
||||
TEST_ASSERT_TRUE(cJSON_IsString(item));
|
||||
|
||||
item->type = cJSON_Array | cJSON_StringIsConst;
|
||||
TEST_ASSERT_FALSE(cJSON_IsArray(NULL));
|
||||
TEST_ASSERT_FALSE(cJSON_IsArray(invalid));
|
||||
TEST_ASSERT_TRUE(cJSON_IsArray(item));
|
||||
|
||||
item->type = cJSON_Object | cJSON_StringIsConst;
|
||||
TEST_ASSERT_FALSE(cJSON_IsObject(NULL));
|
||||
TEST_ASSERT_FALSE(cJSON_IsObject(invalid));
|
||||
TEST_ASSERT_TRUE(cJSON_IsObject(item));
|
||||
|
||||
item->type = cJSON_Raw | cJSON_StringIsConst;
|
||||
TEST_ASSERT_FALSE(cJSON_IsRaw(NULL));
|
||||
TEST_ASSERT_FALSE(cJSON_IsRaw(invalid));
|
||||
TEST_ASSERT_TRUE(cJSON_IsRaw(item));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(cjson_array_foreach_should_loop_over_arrays);
|
||||
RUN_TEST(cjson_array_foreach_should_not_dereference_null_pointer);
|
||||
RUN_TEST(cjson_get_object_item_should_get_object_items);
|
||||
RUN_TEST(cjson_get_object_item_case_sensitive_should_get_object_items);
|
||||
RUN_TEST(typecheck_functions_should_check_type);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
158
tests/parse_array.c
Normal file
158
tests/parse_array.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static cJSON item[1];
|
||||
|
||||
static const unsigned char *error_pointer = NULL;
|
||||
|
||||
static void assert_is_array(cJSON *array_item)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(array_item, "Item is NULL.");
|
||||
|
||||
assert_not_in_list(array_item);
|
||||
assert_has_type(array_item, cJSON_Array);
|
||||
assert_has_no_reference(array_item);
|
||||
assert_has_no_const_string(array_item);
|
||||
assert_has_no_valuestring(array_item);
|
||||
assert_has_no_string(array_item);
|
||||
}
|
||||
|
||||
static void assert_not_array(const char *json)
|
||||
{
|
||||
TEST_ASSERT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer, &global_hooks));
|
||||
assert_is_invalid(item);
|
||||
}
|
||||
|
||||
static void assert_parse_array(const char *json)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer, &global_hooks));
|
||||
assert_is_array(item);
|
||||
}
|
||||
|
||||
static void parse_array_should_parse_empty_arrays(void)
|
||||
{
|
||||
assert_parse_array("[]");
|
||||
assert_has_no_child(item);
|
||||
|
||||
assert_parse_array("[\n\t]");
|
||||
assert_has_no_child(item);
|
||||
}
|
||||
|
||||
|
||||
static void parse_array_should_parse_arrays_with_one_element(void)
|
||||
{
|
||||
|
||||
assert_parse_array("[1]");
|
||||
assert_has_child(item);
|
||||
assert_has_type(item->child, cJSON_Number);
|
||||
reset(item);
|
||||
|
||||
assert_parse_array("[\"hello!\"]");
|
||||
assert_has_child(item);
|
||||
assert_has_type(item->child, cJSON_String);
|
||||
TEST_ASSERT_EQUAL_STRING("hello!", item->child->valuestring);
|
||||
reset(item);
|
||||
|
||||
assert_parse_array("[[]]");
|
||||
assert_has_child(item);
|
||||
assert_is_array(item->child);
|
||||
assert_has_no_child(item->child);
|
||||
reset(item);
|
||||
|
||||
assert_parse_array("[null]");
|
||||
assert_has_child(item);
|
||||
assert_has_type(item->child, cJSON_NULL);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_array_should_parse_arrays_with_multiple_elements(void)
|
||||
{
|
||||
assert_parse_array("[1\t,\n2, 3]");
|
||||
assert_has_child(item);
|
||||
TEST_ASSERT_NOT_NULL(item->child->next);
|
||||
TEST_ASSERT_NOT_NULL(item->child->next->next);
|
||||
TEST_ASSERT_NULL(item->child->next->next->next);
|
||||
assert_has_type(item->child, cJSON_Number);
|
||||
assert_has_type(item->child->next, cJSON_Number);
|
||||
assert_has_type(item->child->next->next, cJSON_Number);
|
||||
reset(item);
|
||||
|
||||
{
|
||||
size_t i = 0;
|
||||
cJSON *node = NULL;
|
||||
int expected_types[7] =
|
||||
{
|
||||
cJSON_Number,
|
||||
cJSON_NULL,
|
||||
cJSON_True,
|
||||
cJSON_False,
|
||||
cJSON_Array,
|
||||
cJSON_String,
|
||||
cJSON_Object
|
||||
};
|
||||
assert_parse_array("[1, null, true, false, [], \"hello\", {}]");
|
||||
|
||||
node = item->child;
|
||||
for (
|
||||
i = 0;
|
||||
(i < (sizeof(expected_types)/sizeof(int)))
|
||||
&& (node != NULL);
|
||||
(void)i++, node = node->next)
|
||||
{
|
||||
TEST_ASSERT_BITS(0xFF, expected_types[i], node->type);
|
||||
}
|
||||
TEST_ASSERT_EQUAL_INT(i, 7);
|
||||
reset(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_array_should_not_parse_non_arrays(void)
|
||||
{
|
||||
assert_not_array("");
|
||||
assert_not_array("[");
|
||||
assert_not_array("]");
|
||||
assert_not_array("{\"hello\":[]}");
|
||||
assert_not_array("42");
|
||||
assert_not_array("3.14");
|
||||
assert_not_array("\"[]hello world!\n\"");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
memset(item, 0, sizeof(cJSON));
|
||||
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(parse_array_should_parse_empty_arrays);
|
||||
RUN_TEST(parse_array_should_parse_arrays_with_one_element);
|
||||
RUN_TEST(parse_array_should_parse_arrays_with_multiple_elements);
|
||||
RUN_TEST(parse_array_should_not_parse_non_arrays);
|
||||
return UNITY_END();
|
||||
}
|
||||
197
tests/parse_examples.c
Normal file
197
tests/parse_examples.c
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static cJSON *parse_file(const char *filename)
|
||||
{
|
||||
cJSON *parsed = NULL;
|
||||
char *content = read_file(filename);
|
||||
|
||||
parsed = cJSON_Parse(content);
|
||||
|
||||
if (content != NULL)
|
||||
{
|
||||
free(content);
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static void do_test(const char *test_name)
|
||||
{
|
||||
char *expected = NULL;
|
||||
char *actual = NULL;
|
||||
cJSON *tree = NULL;
|
||||
|
||||
size_t test_name_length = 0;
|
||||
/* path of the test input */
|
||||
char *test_path = NULL;
|
||||
/* path of the expected output */
|
||||
char *expected_path = NULL;
|
||||
|
||||
test_name_length = strlen(test_name);
|
||||
|
||||
/* allocate file paths */
|
||||
#define TEST_DIR_PATH "inputs/"
|
||||
test_path = (char*)malloc(sizeof(TEST_DIR_PATH) + test_name_length);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(test_path, "Failed to allocate test_path buffer.");
|
||||
expected_path = (char*)malloc(sizeof(TEST_DIR_PATH) + test_name_length + sizeof(".expected"));
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(expected_path, "Failed to allocate expected_path buffer.");
|
||||
|
||||
/* create file paths */
|
||||
sprintf(test_path, TEST_DIR_PATH"%s", test_name);
|
||||
sprintf(expected_path, TEST_DIR_PATH"%s.expected", test_name);
|
||||
|
||||
/* read expected output */
|
||||
expected = read_file(expected_path);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(expected, "Failed to read expected output.");
|
||||
|
||||
/* read and parse test */
|
||||
tree = parse_file(test_path);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to read of parse test.");
|
||||
|
||||
/* print the parsed tree */
|
||||
actual = cJSON_Print(tree);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(actual, "Failed to print tree back to JSON.");
|
||||
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING(expected, actual);
|
||||
|
||||
/* cleanup resources */
|
||||
if (expected != NULL)
|
||||
{
|
||||
free(expected);
|
||||
}
|
||||
if (tree != NULL)
|
||||
{
|
||||
cJSON_Delete(tree);
|
||||
}
|
||||
if (actual != NULL)
|
||||
{
|
||||
free(actual);
|
||||
}
|
||||
if (test_path != NULL)
|
||||
{
|
||||
free(test_path);
|
||||
}
|
||||
if (expected_path != NULL)
|
||||
{
|
||||
free(expected_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void file_test1_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test1");
|
||||
}
|
||||
|
||||
static void file_test2_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test2");
|
||||
}
|
||||
|
||||
static void file_test3_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test3");
|
||||
}
|
||||
|
||||
static void file_test4_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test4");
|
||||
}
|
||||
|
||||
static void file_test5_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test5");
|
||||
}
|
||||
|
||||
static void file_test6_should_not_be_parsed(void)
|
||||
{
|
||||
char *test6 = NULL;
|
||||
cJSON *tree = NULL;
|
||||
|
||||
test6 = read_file("inputs/test6");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(test6, "Failed to read test6 data.");
|
||||
|
||||
tree = cJSON_Parse(test6);
|
||||
TEST_ASSERT_NULL_MESSAGE(tree, "Should fail to parse what is not JSON.");
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(test6, cJSON_GetErrorPtr(), "Error pointer is incorrect.");
|
||||
|
||||
if (test6 != NULL)
|
||||
{
|
||||
free(test6);
|
||||
}
|
||||
if (tree != NULL)
|
||||
{
|
||||
cJSON_Delete(tree);
|
||||
}
|
||||
}
|
||||
|
||||
static void file_test7_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test7");
|
||||
}
|
||||
|
||||
static void file_test8_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test8");
|
||||
}
|
||||
|
||||
static void file_test9_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test9");
|
||||
}
|
||||
|
||||
static void file_test10_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test10");
|
||||
}
|
||||
|
||||
static void file_test11_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test11");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(file_test1_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test2_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test3_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test4_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test5_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test6_should_not_be_parsed);
|
||||
RUN_TEST(file_test7_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test8_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test9_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test10_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test11_should_be_parsed_and_printed);
|
||||
return UNITY_END();
|
||||
}
|
||||
73
tests/parse_hex4.c
Normal file
73
tests/parse_hex4.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static void parse_hex4_should_parse_all_combinations(void)
|
||||
{
|
||||
unsigned int number = 0;
|
||||
unsigned char digits_lower[5];
|
||||
unsigned char digits_upper[5];
|
||||
/* test all combinations */
|
||||
for (number = 0; number <= 0xFFFF; number++)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(4, sprintf((char*)digits_lower, "%.4x", number), "sprintf failed.");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(4, sprintf((char*)digits_upper, "%.4X", number), "sprintf failed.");
|
||||
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(number, parse_hex4(digits_lower), "Failed to parse lowercase digits.");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(number, parse_hex4(digits_upper), "Failed to parse uppercase digits.");
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_hex4_should_parse_mixed_case(void)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beef"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beeF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beEf"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beEF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEef"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEeF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEEf"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEEF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"Beef"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BeeF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BeEf"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BeEF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEef"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEeF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEEf"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEEF"));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(parse_hex4_should_parse_all_combinations);
|
||||
RUN_TEST(parse_hex4_should_parse_mixed_case);
|
||||
return UNITY_END();
|
||||
}
|
||||
106
tests/parse_number.c
Normal file
106
tests/parse_number.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static cJSON item[1];
|
||||
|
||||
static void assert_is_number(cJSON *number_item)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(number_item, "Item is NULL.");
|
||||
|
||||
assert_not_in_list(number_item);
|
||||
assert_has_no_child(number_item);
|
||||
assert_has_type(number_item, cJSON_Number);
|
||||
assert_has_no_reference(number_item);
|
||||
assert_has_no_const_string(number_item);
|
||||
assert_has_no_valuestring(number_item);
|
||||
assert_has_no_string(number_item);
|
||||
}
|
||||
|
||||
static void assert_parse_number(const char *string, int integer, double real)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(parse_number(item, (const unsigned char*)string));
|
||||
assert_is_number(item);
|
||||
TEST_ASSERT_EQUAL_INT(integer, item->valueint);
|
||||
TEST_ASSERT_EQUAL_DOUBLE(real, item->valuedouble);
|
||||
}
|
||||
|
||||
static void parse_number_should_parse_zero(void)
|
||||
{
|
||||
assert_parse_number("0", 0, 0);
|
||||
assert_parse_number("0.0", 0, 0.0);
|
||||
assert_parse_number("-0", 0, -0.0);
|
||||
}
|
||||
|
||||
static void parse_number_should_parse_negative_integers(void)
|
||||
{
|
||||
assert_parse_number("-1", -1, -1);
|
||||
assert_parse_number("-32768", -32768, -32768.0);
|
||||
assert_parse_number("-2147483648", (int)-2147483648.0, -2147483648.0);
|
||||
}
|
||||
|
||||
static void parse_number_should_parse_positive_integers(void)
|
||||
{
|
||||
assert_parse_number("1", 1, 1);
|
||||
assert_parse_number("32767", 32767, 32767.0);
|
||||
assert_parse_number("2147483647", (int)2147483647.0, 2147483647.0);
|
||||
}
|
||||
|
||||
static void parse_number_should_parse_positive_reals(void)
|
||||
{
|
||||
assert_parse_number("0.001", 0, 0.001);
|
||||
assert_parse_number("10e-10", 0, 10e-10);
|
||||
assert_parse_number("10E-10", 0, 10e-10);
|
||||
assert_parse_number("10e10", INT_MAX, 10e10);
|
||||
assert_parse_number("123e+127", INT_MAX, 123e127);
|
||||
assert_parse_number("123e-128", 0, 123e-128);
|
||||
}
|
||||
|
||||
static void parse_number_should_parse_negative_reals(void)
|
||||
{
|
||||
assert_parse_number("-0.001", 0, -0.001);
|
||||
assert_parse_number("-10e-10", 0, -10e-10);
|
||||
assert_parse_number("-10E-10", 0, -10e-10);
|
||||
assert_parse_number("-10e20", INT_MIN, -10e20);
|
||||
assert_parse_number("-123e+127", INT_MIN, -123e127);
|
||||
assert_parse_number("-123e-128", 0, -123e-128);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
memset(item, 0, sizeof(cJSON));
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(parse_number_should_parse_zero);
|
||||
RUN_TEST(parse_number_should_parse_negative_integers);
|
||||
RUN_TEST(parse_number_should_parse_positive_integers);
|
||||
RUN_TEST(parse_number_should_parse_positive_reals);
|
||||
RUN_TEST(parse_number_should_parse_negative_reals);
|
||||
return UNITY_END();
|
||||
}
|
||||
168
tests/parse_object.c
Normal file
168
tests/parse_object.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static cJSON item[1];
|
||||
|
||||
static const unsigned char *error_pointer = NULL;
|
||||
|
||||
static void assert_is_object(cJSON *object_item)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(object_item, "Item is NULL.");
|
||||
|
||||
assert_not_in_list(object_item);
|
||||
assert_has_type(object_item, cJSON_Object);
|
||||
assert_has_no_reference(object_item);
|
||||
assert_has_no_const_string(object_item);
|
||||
assert_has_no_valuestring(object_item);
|
||||
assert_has_no_string(object_item);
|
||||
}
|
||||
|
||||
static void assert_is_child(cJSON *child_item, const char *name, int type)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(child_item, "Child item is NULL.");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(child_item->string, "Child item doesn't have a name.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(name, child_item->string, "Child item has the wrong name.");
|
||||
TEST_ASSERT_BITS(0xFF, type, child_item->type);
|
||||
}
|
||||
|
||||
static void assert_not_object(const char *json)
|
||||
{
|
||||
TEST_ASSERT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer, &global_hooks));
|
||||
assert_is_invalid(item);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void assert_parse_object(const char *json)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer, &global_hooks));
|
||||
assert_is_object(item);
|
||||
}
|
||||
|
||||
static void parse_object_should_parse_empty_objects(void)
|
||||
{
|
||||
assert_parse_object("{}");
|
||||
assert_has_no_child(item);
|
||||
reset(item);
|
||||
|
||||
assert_parse_object("{\n\t}");
|
||||
assert_has_no_child(item);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_object_should_parse_objects_with_one_element(void)
|
||||
{
|
||||
|
||||
assert_parse_object("{\"one\":1}");
|
||||
assert_is_child(item->child, "one", cJSON_Number);
|
||||
reset(item);
|
||||
|
||||
assert_parse_object("{\"hello\":\"world!\"}");
|
||||
assert_is_child(item->child, "hello", cJSON_String);
|
||||
reset(item);
|
||||
|
||||
assert_parse_object("{\"array\":[]}");
|
||||
assert_is_child(item->child, "array", cJSON_Array);
|
||||
reset(item);
|
||||
|
||||
assert_parse_object("{\"null\":null}");
|
||||
assert_is_child(item->child, "null", cJSON_NULL);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_object_should_parse_objects_with_multiple_elements(void)
|
||||
{
|
||||
assert_parse_object("{\"one\":1\t,\t\"two\"\n:2, \"three\":3}");
|
||||
assert_is_child(item->child, "one", cJSON_Number);
|
||||
assert_is_child(item->child->next, "two", cJSON_Number);
|
||||
assert_is_child(item->child->next->next, "three", cJSON_Number);
|
||||
reset(item);
|
||||
|
||||
{
|
||||
size_t i = 0;
|
||||
cJSON *node = NULL;
|
||||
int expected_types[7] =
|
||||
{
|
||||
cJSON_Number,
|
||||
cJSON_NULL,
|
||||
cJSON_True,
|
||||
cJSON_False,
|
||||
cJSON_Array,
|
||||
cJSON_String,
|
||||
cJSON_Object
|
||||
};
|
||||
const char *expected_names[7] =
|
||||
{
|
||||
"one",
|
||||
"NULL",
|
||||
"TRUE",
|
||||
"FALSE",
|
||||
"array",
|
||||
"world",
|
||||
"object"
|
||||
};
|
||||
assert_parse_object("{\"one\":1, \"NULL\":null, \"TRUE\":true, \"FALSE\":false, \"array\":[], \"world\":\"hello\", \"object\":{}}");
|
||||
|
||||
node = item->child;
|
||||
for (
|
||||
i = 0;
|
||||
(i < (sizeof(expected_types)/sizeof(int)))
|
||||
&& (node != NULL);
|
||||
(void)i++, node = node->next)
|
||||
{
|
||||
assert_is_child(node, expected_names[i], expected_types[i]);
|
||||
}
|
||||
TEST_ASSERT_EQUAL_INT(i, 7);
|
||||
reset(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_object_should_not_parse_non_objects(void)
|
||||
{
|
||||
assert_not_object("");
|
||||
assert_not_object("{");
|
||||
assert_not_object("}");
|
||||
assert_not_object("[\"hello\",{}]");
|
||||
assert_not_object("42");
|
||||
assert_not_object("3.14");
|
||||
assert_not_object("\"{}hello world!\n\"");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
memset(item, 0, sizeof(cJSON));
|
||||
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(parse_object_should_parse_empty_objects);
|
||||
RUN_TEST(parse_object_should_not_parse_non_objects);
|
||||
RUN_TEST(parse_object_should_parse_objects_with_multiple_elements);
|
||||
RUN_TEST(parse_object_should_parse_objects_with_one_element);
|
||||
return UNITY_END();
|
||||
}
|
||||
125
tests/parse_string.c
Normal file
125
tests/parse_string.c
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static cJSON item[1];
|
||||
|
||||
static const unsigned char *error_pointer = NULL;
|
||||
|
||||
static void assert_is_string(cJSON *string_item)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(string_item, "Item is NULL.");
|
||||
|
||||
assert_not_in_list(string_item);
|
||||
assert_has_no_child(string_item);
|
||||
assert_has_type(string_item, cJSON_String);
|
||||
assert_has_no_reference(string_item);
|
||||
assert_has_no_const_string(string_item);
|
||||
assert_has_valuestring(string_item);
|
||||
assert_has_no_string(string_item);
|
||||
}
|
||||
|
||||
static void assert_parse_string(const char *string, const char *expected)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer, &global_hooks), "Couldn't parse string.");
|
||||
assert_is_string(item);
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, item->valuestring, "The parsed result isn't as expected.");
|
||||
global_hooks.deallocate(item->valuestring);
|
||||
item->valuestring = NULL;
|
||||
}
|
||||
|
||||
#define assert_not_parse_string(string) \
|
||||
TEST_ASSERT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer, &global_hooks), "Malformed string should not be accepted");\
|
||||
assert_is_invalid(item)
|
||||
|
||||
|
||||
|
||||
static void parse_string_should_parse_strings(void)
|
||||
{
|
||||
assert_parse_string("\"\"", "");
|
||||
assert_parse_string(
|
||||
"\" !\\\"#$%&'()*+,-./\\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_'abcdefghijklmnopqrstuvwxyz{|}~\"",
|
||||
" !\"#$%&'()*+,-.//0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'abcdefghijklmnopqrstuvwxyz{|}~");
|
||||
assert_parse_string(
|
||||
"\"\\\"\\\\\\/\\b\\f\\n\\r\\t\\u20AC\\u732b\"",
|
||||
"\"\\/\b\f\n\r\t€猫");
|
||||
reset(item);
|
||||
assert_parse_string("\"\b\f\n\r\t\"", "\b\f\n\r\t");
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_string_should_parse_utf16_surrogate_pairs(void)
|
||||
{
|
||||
assert_parse_string("\"\\uD83D\\udc31\"", "🐱");
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_string_should_not_parse_non_strings(void)
|
||||
{
|
||||
assert_not_parse_string("this\" is not a string\"");
|
||||
reset(item);
|
||||
assert_not_parse_string("");
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_string_should_not_parse_invalid_backslash(void)
|
||||
{
|
||||
assert_not_parse_string("Abcdef\\123");
|
||||
reset(item);
|
||||
assert_not_parse_string("Abcdef\\e23");
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_string_should_not_overflow_with_closing_backslash(void)
|
||||
{
|
||||
assert_not_parse_string("\"000000000000000000\\");
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_string_should_parse_bug_94(void)
|
||||
{
|
||||
const char string[] = "\"~!@\\\\#$%^&*()\\\\\\\\-\\\\+{}[]:\\\\;\\\\\\\"\\\\<\\\\>?/.,DC=ad,DC=com\"";
|
||||
assert_parse_string(string, "~!@\\#$%^&*()\\\\-\\+{}[]:\\;\\\"\\<\\>?/.,DC=ad,DC=com");
|
||||
reset(item);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item and error pointer */
|
||||
memset(item, 0, sizeof(cJSON));
|
||||
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(parse_string_should_parse_strings);
|
||||
RUN_TEST(parse_string_should_parse_utf16_surrogate_pairs);
|
||||
RUN_TEST(parse_string_should_not_parse_non_strings);
|
||||
RUN_TEST(parse_string_should_not_parse_invalid_backslash);
|
||||
RUN_TEST(parse_string_should_parse_bug_94);
|
||||
RUN_TEST(parse_string_should_not_overflow_with_closing_backslash);
|
||||
return UNITY_END();
|
||||
}
|
||||
108
tests/parse_value.c
Normal file
108
tests/parse_value.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static cJSON item[1];
|
||||
const unsigned char *error_pointer = NULL;
|
||||
|
||||
static void assert_is_value(cJSON *value_item, int type)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(value_item, "Item is NULL.");
|
||||
|
||||
assert_not_in_list(value_item);
|
||||
assert_has_type(value_item, type);
|
||||
assert_has_no_reference(value_item);
|
||||
assert_has_no_const_string(value_item);
|
||||
assert_has_no_string(value_item);
|
||||
}
|
||||
|
||||
static void assert_parse_value(const char *string, int type)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(parse_value(item, (const unsigned char*)string, &error_pointer, &global_hooks));
|
||||
assert_is_value(item, type);
|
||||
}
|
||||
|
||||
static void parse_value_should_parse_null(void)
|
||||
{
|
||||
assert_parse_value("null", cJSON_NULL);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_value_should_parse_true(void)
|
||||
{
|
||||
assert_parse_value("true", cJSON_True);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_value_should_parse_false(void)
|
||||
{
|
||||
assert_parse_value("false", cJSON_False);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_value_should_parse_number(void)
|
||||
{
|
||||
assert_parse_value("1.5", cJSON_Number);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_value_should_parse_string(void)
|
||||
{
|
||||
assert_parse_value("\"\"", cJSON_String);
|
||||
reset(item);
|
||||
assert_parse_value("\"hello\"", cJSON_String);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_value_should_parse_array(void)
|
||||
{
|
||||
assert_parse_value("[]", cJSON_Array);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_value_should_parse_object(void)
|
||||
{
|
||||
assert_parse_value("{}", cJSON_Object);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
memset(item, 0, sizeof(cJSON));
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(parse_value_should_parse_null);
|
||||
RUN_TEST(parse_value_should_parse_true);
|
||||
RUN_TEST(parse_value_should_parse_false);
|
||||
RUN_TEST(parse_value_should_parse_number);
|
||||
RUN_TEST(parse_value_should_parse_string);
|
||||
RUN_TEST(parse_value_should_parse_array);
|
||||
RUN_TEST(parse_value_should_parse_object);
|
||||
return UNITY_END();
|
||||
}
|
||||
92
tests/print_array.c
Normal file
92
tests/print_array.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static void assert_print_array(const char * const expected, const char * const input)
|
||||
{
|
||||
unsigned char printed_unformatted[1024];
|
||||
unsigned char printed_formatted[1024];
|
||||
|
||||
const unsigned char *error_pointer;
|
||||
cJSON item[1];
|
||||
|
||||
printbuffer formatted_buffer;
|
||||
printbuffer unformatted_buffer;
|
||||
|
||||
/* buffer for formatted printing */
|
||||
formatted_buffer.buffer = printed_formatted;
|
||||
formatted_buffer.length = sizeof(printed_formatted);
|
||||
formatted_buffer.offset = 0;
|
||||
formatted_buffer.noalloc = true;
|
||||
|
||||
/* buffer for unformatted printing */
|
||||
unformatted_buffer.buffer = printed_unformatted;
|
||||
unformatted_buffer.length = sizeof(printed_unformatted);
|
||||
unformatted_buffer.offset = 0;
|
||||
unformatted_buffer.noalloc = true;
|
||||
|
||||
memset(item, 0, sizeof(item));
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(parse_array(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse array.");
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_array(item, 0, false, &unformatted_buffer, &global_hooks), "Failed to print unformatted string.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted array is not correct.");
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_array(item, 0, true, &formatted_buffer, &global_hooks), "Failed to print formatted string.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted array is not correct.");
|
||||
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void print_array_should_print_empty_arrays(void)
|
||||
{
|
||||
assert_print_array("[]", "[]");
|
||||
}
|
||||
|
||||
static void print_array_should_print_arrays_with_one_element(void)
|
||||
{
|
||||
|
||||
assert_print_array("[1]", "[1]");
|
||||
assert_print_array("[\"hello!\"]", "[\"hello!\"]");
|
||||
assert_print_array("[[]]", "[[]]");
|
||||
assert_print_array("[null]", "[null]");
|
||||
}
|
||||
|
||||
static void print_array_should_print_arrays_with_multiple_elements(void)
|
||||
{
|
||||
assert_print_array("[1, 2, 3]", "[1,2,3]");
|
||||
assert_print_array("[1, null, true, false, [], \"hello\", {\n\t}]", "[1,null,true,false,[],\"hello\",{}]");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(print_array_should_print_empty_arrays);
|
||||
RUN_TEST(print_array_should_print_arrays_with_one_element);
|
||||
RUN_TEST(print_array_should_print_arrays_with_multiple_elements);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
119
tests/print_number.c
Normal file
119
tests/print_number.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static void assert_print_number(const char *expected, double input)
|
||||
{
|
||||
unsigned char printed[1024];
|
||||
cJSON item[1];
|
||||
printbuffer buffer;
|
||||
buffer.buffer = printed;
|
||||
buffer.length = sizeof(printed);
|
||||
buffer.offset = 0;
|
||||
buffer.noalloc = true;
|
||||
|
||||
memset(item, 0, sizeof(item));
|
||||
cJSON_SetNumberValue(item, input);
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_number(item, &buffer, &global_hooks), "Failed to print number.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buffer.buffer, "Printed number is not as expected.");
|
||||
}
|
||||
|
||||
static void print_number_should_print_zero(void)
|
||||
{
|
||||
assert_print_number("0", 0);
|
||||
}
|
||||
|
||||
static void print_number_should_print_negative_integers(void)
|
||||
{
|
||||
assert_print_number("-1", -1);
|
||||
assert_print_number("-32768", -32768);
|
||||
assert_print_number("-2147483648", -2147483648);
|
||||
}
|
||||
|
||||
static void print_number_should_print_positive_integers(void)
|
||||
{
|
||||
assert_print_number("1", 1);
|
||||
assert_print_number("32767", 32767);
|
||||
assert_print_number("2147483647", 2147483647);
|
||||
}
|
||||
|
||||
static void print_number_should_print_positive_reals(void)
|
||||
{
|
||||
assert_print_number("0.123", 0.123);
|
||||
assert_print_number("1.000000e-09", 10e-10);
|
||||
assert_print_number("1000000000000", 10e11);
|
||||
assert_print_number("1.230000e+129", 123e+127);
|
||||
assert_print_number("0", 123e-128); /* TODO: Maybe this shouldn't be 0 */
|
||||
}
|
||||
|
||||
static void print_number_should_print_negative_reals(void)
|
||||
{
|
||||
assert_print_number("-0.0123", -0.0123);
|
||||
assert_print_number("-1.000000e-09", -10e-10);
|
||||
assert_print_number("-1000000000000000000000", -10e20);
|
||||
assert_print_number("-1.230000e+129", -123e+127);
|
||||
assert_print_number("-1.230000e-126", -123e-128);
|
||||
}
|
||||
|
||||
static void print_number_should_print_non_number(void)
|
||||
{
|
||||
TEST_IGNORE();
|
||||
/* FIXME: Cannot test this easily in C89! */
|
||||
/* assert_print_number("null", NaN); */
|
||||
/* assert_print_number("null", INFTY); */
|
||||
/* assert_print_number("null", -INFTY); */
|
||||
}
|
||||
|
||||
static void trim_trailing_zeroes_should_trim_trailing_zeroes(void)
|
||||
{
|
||||
printbuffer buffer;
|
||||
unsigned char number[100];
|
||||
buffer.length = sizeof(number);
|
||||
buffer.buffer = number;
|
||||
|
||||
strcpy((char*)number, "10.00");
|
||||
buffer.offset = sizeof("10.00") - 1;
|
||||
TEST_ASSERT_TRUE(trim_trailing_zeroes(&buffer));
|
||||
TEST_ASSERT_EQUAL_UINT8('\0', buffer.buffer[buffer.offset]);
|
||||
TEST_ASSERT_EQUAL_STRING("10", number);
|
||||
TEST_ASSERT_EQUAL_UINT(sizeof("10") - 1, buffer.offset);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(print_number_should_print_zero);
|
||||
RUN_TEST(print_number_should_print_negative_integers);
|
||||
RUN_TEST(print_number_should_print_positive_integers);
|
||||
RUN_TEST(print_number_should_print_positive_reals);
|
||||
RUN_TEST(print_number_should_print_negative_reals);
|
||||
RUN_TEST(print_number_should_print_non_number);
|
||||
RUN_TEST(trim_trailing_zeroes_should_trim_trailing_zeroes);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
92
tests/print_object.c
Normal file
92
tests/print_object.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static void assert_print_object(const char * const expected, const char * const input)
|
||||
{
|
||||
unsigned char printed_unformatted[1024];
|
||||
unsigned char printed_formatted[1024];
|
||||
|
||||
const unsigned char *error_pointer;
|
||||
cJSON item[1];
|
||||
|
||||
printbuffer formatted_buffer;
|
||||
printbuffer unformatted_buffer;
|
||||
|
||||
/* buffer for formatted printing */
|
||||
formatted_buffer.buffer = printed_formatted;
|
||||
formatted_buffer.length = sizeof(printed_formatted);
|
||||
formatted_buffer.offset = 0;
|
||||
formatted_buffer.noalloc = true;
|
||||
|
||||
/* buffer for unformatted printing */
|
||||
unformatted_buffer.buffer = printed_unformatted;
|
||||
unformatted_buffer.length = sizeof(printed_unformatted);
|
||||
unformatted_buffer.offset = 0;
|
||||
unformatted_buffer.noalloc = true;
|
||||
|
||||
memset(item, 0, sizeof(item));
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(parse_object(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse object.");
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_object(item, 0, false, &unformatted_buffer, &global_hooks), "Failed to print unformatted string.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted object is not correct.");
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_object(item, 0, true, &formatted_buffer, &global_hooks), "Failed to print formatted string.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted ojbect is not correct.");
|
||||
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void print_object_should_print_empty_objects(void)
|
||||
{
|
||||
assert_print_object("{\n}", "{}");
|
||||
}
|
||||
|
||||
static void print_object_should_print_objects_with_one_element(void)
|
||||
{
|
||||
|
||||
assert_print_object("{\n\t\"one\":\t1\n}", "{\"one\":1}");
|
||||
assert_print_object("{\n\t\"hello\":\t\"world!\"\n}", "{\"hello\":\"world!\"}");
|
||||
assert_print_object("{\n\t\"array\":\t[]\n}", "{\"array\":[]}");
|
||||
assert_print_object("{\n\t\"null\":\tnull\n}", "{\"null\":null}");
|
||||
}
|
||||
|
||||
static void print_object_should_print_objects_with_multiple_elements(void)
|
||||
{
|
||||
assert_print_object("{\n\t\"one\":\t1,\n\t\"two\":\t2,\n\t\"three\":\t3\n}", "{\"one\":1,\"two\":2,\"three\":3}");
|
||||
assert_print_object("{\n\t\"one\":\t1,\n\t\"NULL\":\tnull,\n\t\"TRUE\":\ttrue,\n\t\"FALSE\":\tfalse,\n\t\"array\":\t[],\n\t\"world\":\t\"hello\",\n\t\"object\":\t{\n\t}\n}", "{\"one\":1,\"NULL\":null,\"TRUE\":true,\"FALSE\":false,\"array\":[],\"world\":\"hello\",\"object\":{}}");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(print_object_should_print_empty_objects);
|
||||
RUN_TEST(print_object_should_print_objects_with_one_element);
|
||||
RUN_TEST(print_object_should_print_objects_with_multiple_elements);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
77
tests/print_string.c
Normal file
77
tests/print_string.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static void assert_print_string(const char *expected, const char *input)
|
||||
{
|
||||
unsigned char printed[1024];
|
||||
printbuffer buffer;
|
||||
buffer.buffer = printed;
|
||||
buffer.length = sizeof(printed);
|
||||
buffer.offset = 0;
|
||||
buffer.noalloc = true;
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_string_ptr((const unsigned char*)input, &buffer, &global_hooks), "Failed to print string.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed, "The printed string isn't as expected.");
|
||||
}
|
||||
|
||||
static void print_string_should_print_empty_strings(void)
|
||||
{
|
||||
assert_print_string("\"\"", "");
|
||||
assert_print_string("\"\"", NULL);
|
||||
}
|
||||
|
||||
static void print_string_should_print_ascii(void)
|
||||
{
|
||||
char ascii[0x7F];
|
||||
size_t i = 1;
|
||||
|
||||
/* create ascii table */
|
||||
for (i = 1; i < 0x7F; i++)
|
||||
{
|
||||
ascii[i-1] = (char)i;
|
||||
}
|
||||
ascii[0x7F-1] = '\0';
|
||||
|
||||
assert_print_string("\"\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"",
|
||||
ascii);
|
||||
}
|
||||
|
||||
static void print_string_should_print_utf8(void)
|
||||
{
|
||||
assert_print_string("\"ü猫慕\"", "ü猫慕");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(print_string_should_print_empty_strings);
|
||||
RUN_TEST(print_string_should_print_ascii);
|
||||
RUN_TEST(print_string_should_print_utf8);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
102
tests/print_value.c
Normal file
102
tests/print_value.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static void assert_print_value(const char *input)
|
||||
{
|
||||
unsigned char printed[1024];
|
||||
const unsigned char *error_pointer = NULL;
|
||||
cJSON item[1];
|
||||
printbuffer buffer;
|
||||
buffer.buffer = printed;
|
||||
buffer.length = sizeof(printed);
|
||||
buffer.offset = 0;
|
||||
buffer.noalloc = true;
|
||||
|
||||
memset(item, 0, sizeof(item));
|
||||
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(parse_value(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse value.");
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_value(item, 0, false, &buffer, &global_hooks), "Failed to print value.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(input, buffer.buffer, "Printed value is not as expected.");
|
||||
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void print_value_should_print_null(void)
|
||||
{
|
||||
assert_print_value("null");
|
||||
}
|
||||
|
||||
static void print_value_should_print_true(void)
|
||||
{
|
||||
assert_print_value("true");
|
||||
}
|
||||
|
||||
static void print_value_should_print_false(void)
|
||||
{
|
||||
assert_print_value("false");
|
||||
}
|
||||
|
||||
static void print_value_should_print_number(void)
|
||||
{
|
||||
assert_print_value("1.5");
|
||||
}
|
||||
|
||||
static void print_value_should_print_string(void)
|
||||
{
|
||||
assert_print_value("\"\"");
|
||||
assert_print_value("\"hello\"");
|
||||
}
|
||||
|
||||
static void print_value_should_print_array(void)
|
||||
{
|
||||
assert_print_value("[]");
|
||||
}
|
||||
|
||||
static void print_value_should_print_object(void)
|
||||
{
|
||||
assert_print_value("{}");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(print_value_should_print_null);
|
||||
RUN_TEST(print_value_should_print_true);
|
||||
RUN_TEST(print_value_should_print_false);
|
||||
RUN_TEST(print_value_should_print_number);
|
||||
RUN_TEST(print_value_should_print_string);
|
||||
RUN_TEST(print_value_should_print_array);
|
||||
RUN_TEST(print_value_should_print_object);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
30
tests/unity/.gitattributes
vendored
Normal file
30
tests/unity/.gitattributes
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
* text=auto
|
||||
|
||||
# These files are text and should be normalized (convert crlf to lf)
|
||||
*.rb text
|
||||
*.test text
|
||||
*.c text
|
||||
*.cpp text
|
||||
*.h text
|
||||
*.txt text
|
||||
*.yml text
|
||||
*.s79 text
|
||||
*.bat text
|
||||
*.xcl text
|
||||
*.inc text
|
||||
*.info text
|
||||
*.md text
|
||||
makefile text
|
||||
rakefile text
|
||||
|
||||
|
||||
#These files are binary and should not be normalized
|
||||
*.doc binary
|
||||
*.odt binary
|
||||
*.pdf binary
|
||||
*.ewd binary
|
||||
*.eww binary
|
||||
*.dni binary
|
||||
*.wsdt binary
|
||||
*.dbgdt binary
|
||||
*.mac binary
|
||||
9
tests/unity/.gitignore
vendored
Normal file
9
tests/unity/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
build/
|
||||
test/sandbox
|
||||
.DS_Store
|
||||
examples/example_1/test1.exe
|
||||
examples/example_1/test2.exe
|
||||
examples/example_2/all_tests.exe
|
||||
examples/example_1/test1.out
|
||||
examples/example_1/test2.out
|
||||
examples/example_2/all_tests.out
|
||||
25
tests/unity/.travis.yml
Normal file
25
tests/unity/.travis.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
language: c
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
compiler: clang
|
||||
osx_image: xcode7.3
|
||||
- os: linux
|
||||
dist: trusty
|
||||
compiler: gcc
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then rvm install 2.1 && rvm use 2.1 && ruby -v; fi
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install --assume-yes --quiet gcc-multilib; fi
|
||||
install: gem install rspec
|
||||
script:
|
||||
- cd test && rake ci
|
||||
- make -s
|
||||
- make -s DEBUG=-m32
|
||||
- cd ../extras/fixture/test && rake ci
|
||||
- make -s default noStdlibMalloc
|
||||
- make -s C89
|
||||
- cd ../../../examples/example_1 && make -s ci
|
||||
- cd ../example_2 && make -s ci
|
||||
- cd ../example_3 && rake
|
||||
211
tests/unity/README.md
Normal file
211
tests/unity/README.md
Normal file
@@ -0,0 +1,211 @@
|
||||
Unity Test API
|
||||
==============
|
||||
|
||||
[](https://travis-ci.org/ThrowTheSwitch/Unity)
|
||||
__Copyright (c) 2007 - 2014 Unity Project by Mike Karlesky, Mark VanderVoord, and Greg Williams__
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
|
||||
RUN_TEST(func, linenum)
|
||||
|
||||
Each Test is run within the macro `RUN_TEST`. This macro performs necessary setup before the test is called and handles cleanup and result tabulation afterwards.
|
||||
|
||||
Ignoring Tests
|
||||
--------------
|
||||
|
||||
There are times when a test is incomplete or not valid for some reason. At these times, TEST_IGNORE can be called. Control will immediately be returned to the caller of the test, and no failures will be returned.
|
||||
|
||||
TEST_IGNORE()
|
||||
|
||||
Ignore this test and return immediately
|
||||
|
||||
TEST_IGNORE_MESSAGE (message)
|
||||
|
||||
Ignore this test and return immediately. Output a message stating why the test was ignored.
|
||||
|
||||
Aborting Tests
|
||||
--------------
|
||||
|
||||
There are times when a test will contain an infinite loop on error conditions, or there may be reason to escape from the test early without executing the rest of the test. A pair of macros support this functionality in Unity. The first `TEST_PROTECT` sets up the feature, and handles emergency abort cases. `TEST_ABORT` can then be used at any time within the tests to return to the last `TEST_PROTECT` call.
|
||||
|
||||
TEST_PROTECT()
|
||||
|
||||
Setup and Catch macro
|
||||
|
||||
TEST_ABORT()
|
||||
|
||||
Abort Test macro
|
||||
|
||||
Example:
|
||||
|
||||
main()
|
||||
{
|
||||
if (TEST_PROTECT())
|
||||
{
|
||||
MyTest();
|
||||
}
|
||||
}
|
||||
|
||||
If MyTest calls `TEST_ABORT`, program control will immediately return to `TEST_PROTECT` with a return value of zero.
|
||||
|
||||
|
||||
Unity Assertion Summary
|
||||
=======================
|
||||
|
||||
Basic Validity Tests
|
||||
--------------------
|
||||
|
||||
TEST_ASSERT_TRUE(condition)
|
||||
|
||||
Evaluates whatever code is in condition and fails if it evaluates to false
|
||||
|
||||
TEST_ASSERT_FALSE(condition)
|
||||
|
||||
Evaluates whatever code is in condition and fails if it evaluates to true
|
||||
|
||||
TEST_ASSERT(condition)
|
||||
|
||||
Another way of calling `TEST_ASSERT_TRUE`
|
||||
|
||||
TEST_ASSERT_UNLESS(condition)
|
||||
|
||||
Another way of calling `TEST_ASSERT_FALSE`
|
||||
|
||||
TEST_FAIL()
|
||||
TEST_FAIL_MESSAGE(message)
|
||||
|
||||
This test is automatically marked as a failure. The message is output stating why.
|
||||
|
||||
Numerical Assertions: Integers
|
||||
------------------------------
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(expected, actual)
|
||||
TEST_ASSERT_EQUAL_INT8(expected, actual)
|
||||
TEST_ASSERT_EQUAL_INT16(expected, actual)
|
||||
TEST_ASSERT_EQUAL_INT32(expected, actual)
|
||||
TEST_ASSERT_EQUAL_INT64(expected, actual)
|
||||
|
||||
Compare two integers for equality and display errors as signed integers. A cast will be performed
|
||||
to your natural integer size so often this can just be used. When you need to specify the exact size,
|
||||
like when comparing arrays, you can use a specific version:
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT(expected, actual)
|
||||
TEST_ASSERT_EQUAL_UINT8(expected, actual)
|
||||
TEST_ASSERT_EQUAL_UINT16(expected, actual)
|
||||
TEST_ASSERT_EQUAL_UINT32(expected, actual)
|
||||
TEST_ASSERT_EQUAL_UINT64(expected, actual)
|
||||
|
||||
Compare two integers for equality and display errors as unsigned integers. Like INT, there are
|
||||
variants for different sizes also.
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX(expected, actual)
|
||||
TEST_ASSERT_EQUAL_HEX8(expected, actual)
|
||||
TEST_ASSERT_EQUAL_HEX16(expected, actual)
|
||||
TEST_ASSERT_EQUAL_HEX32(expected, actual)
|
||||
TEST_ASSERT_EQUAL_HEX64(expected, actual)
|
||||
|
||||
Compares two integers for equality and display errors as hexadecimal. Like the other integer comparisons,
|
||||
you can specify the size... here the size will also effect how many nibbles are shown (for example, `HEX16`
|
||||
will show 4 nibbles).
|
||||
|
||||
_ARRAY
|
||||
|
||||
You can append `_ARRAY` to any of these macros to make an array comparison of that type. Here you will
|
||||
need to care a bit more about the actual size of the value being checked. You will also specify an
|
||||
additional argument which is the number of elements to compare. For example:
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, elements)
|
||||
|
||||
TEST_ASSERT_EQUAL(expected, actual)
|
||||
|
||||
Another way of calling TEST_ASSERT_EQUAL_INT
|
||||
|
||||
TEST_ASSERT_INT_WITHIN(delta, expected, actual)
|
||||
|
||||
Asserts that the actual value is within plus or minus delta of the expected value. This also comes in
|
||||
size specific variants.
|
||||
|
||||
|
||||
Numerical Assertions: Bitwise
|
||||
-----------------------------
|
||||
|
||||
TEST_ASSERT_BITS(mask, expected, actual)
|
||||
|
||||
Use an integer mask to specify which bits should be compared between two other integers. High bits in the mask are compared, low bits ignored.
|
||||
|
||||
TEST_ASSERT_BITS_HIGH(mask, actual)
|
||||
|
||||
Use an integer mask to specify which bits should be inspected to determine if they are all set high. High bits in the mask are compared, low bits ignored.
|
||||
|
||||
TEST_ASSERT_BITS_LOW(mask, actual)
|
||||
|
||||
Use an integer mask to specify which bits should be inspected to determine if they are all set low. High bits in the mask are compared, low bits ignored.
|
||||
|
||||
TEST_ASSERT_BIT_HIGH(bit, actual)
|
||||
|
||||
Test a single bit and verify that it is high. The bit is specified 0-31 for a 32-bit integer.
|
||||
|
||||
TEST_ASSERT_BIT_LOW(bit, actual)
|
||||
|
||||
Test a single bit and verify that it is low. The bit is specified 0-31 for a 32-bit integer.
|
||||
|
||||
Numerical Assertions: Floats
|
||||
----------------------------
|
||||
|
||||
TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual)
|
||||
|
||||
Asserts that the actual value is within plus or minus delta of the expected value.
|
||||
|
||||
TEST_ASSERT_EQUAL_FLOAT(expected, actual)
|
||||
TEST_ASSERT_EQUAL_DOUBLE(expected, actual)
|
||||
|
||||
Asserts that two floating point values are "equal" within a small % delta of the expected value.
|
||||
|
||||
String Assertions
|
||||
-----------------
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING(expected, actual)
|
||||
|
||||
Compare two null-terminate strings. Fail if any character is different or if the lengths are different.
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len)
|
||||
|
||||
Compare two strings. Fail if any character is different, stop comparing after len characters.
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message)
|
||||
|
||||
Compare two null-terminate strings. Fail if any character is different or if the lengths are different. Output a custom message on failure.
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message)
|
||||
|
||||
Compare two strings. Fail if any character is different, stop comparing after len characters. Output a custom message on failure.
|
||||
|
||||
Pointer Assertions
|
||||
------------------
|
||||
|
||||
Most pointer operations can be performed by simply using the integer comparisons above. However, a couple of special cases are added for clarity.
|
||||
|
||||
TEST_ASSERT_NULL(pointer)
|
||||
|
||||
Fails if the pointer is not equal to NULL
|
||||
|
||||
TEST_ASSERT_NOT_NULL(pointer)
|
||||
|
||||
Fails if the pointer is equal to NULL
|
||||
|
||||
Memory Assertions
|
||||
-----------------
|
||||
|
||||
TEST_ASSERT_EQUAL_MEMORY(expected, actual, len)
|
||||
|
||||
Compare two blocks of memory. This is a good generic assertion for types that can't be coerced into acting like
|
||||
standard types... but since it's a memory compare, you have to be careful that your data types are packed.
|
||||
|
||||
_MESSAGE
|
||||
--------
|
||||
|
||||
you can append _MESSAGE to any of the macros to make them take an additional argument. This argument
|
||||
is a string that will be printed at the end of the failure strings. This is useful for specifying more
|
||||
information about the problem.
|
||||
|
||||
115
tests/unity/auto/colour_prompt.rb
Normal file
115
tests/unity/auto/colour_prompt.rb
Normal file
@@ -0,0 +1,115 @@
|
||||
# ==========================================
|
||||
# Unity Project - A Test Framework for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
if RUBY_PLATFORM =~/(win|w)32$/
|
||||
begin
|
||||
require 'Win32API'
|
||||
rescue LoadError
|
||||
puts "ERROR! \"Win32API\" library not found"
|
||||
puts "\"Win32API\" is required for colour on a windows machine"
|
||||
puts " try => \"gem install Win32API\" on the command line"
|
||||
puts
|
||||
end
|
||||
# puts
|
||||
# puts 'Windows Environment Detected...'
|
||||
# puts 'Win32API Library Found.'
|
||||
# puts
|
||||
end
|
||||
|
||||
class ColourCommandLine
|
||||
def initialize
|
||||
if RUBY_PLATFORM =~/(win|w)32$/
|
||||
get_std_handle = Win32API.new("kernel32", "GetStdHandle", ['L'], 'L')
|
||||
@set_console_txt_attrb =
|
||||
Win32API.new("kernel32","SetConsoleTextAttribute",['L','N'], 'I')
|
||||
@hout = get_std_handle.call(-11)
|
||||
end
|
||||
end
|
||||
|
||||
def change_to(new_colour)
|
||||
if RUBY_PLATFORM =~/(win|w)32$/
|
||||
@set_console_txt_attrb.call(@hout,self.win32_colour(new_colour))
|
||||
else
|
||||
"\033[30;#{posix_colour(new_colour)};22m"
|
||||
end
|
||||
end
|
||||
|
||||
def win32_colour(colour)
|
||||
case colour
|
||||
when :black then 0
|
||||
when :dark_blue then 1
|
||||
when :dark_green then 2
|
||||
when :dark_cyan then 3
|
||||
when :dark_red then 4
|
||||
when :dark_purple then 5
|
||||
when :dark_yellow, :narrative then 6
|
||||
when :default_white, :default, :dark_white then 7
|
||||
when :silver then 8
|
||||
when :blue then 9
|
||||
when :green, :success then 10
|
||||
when :cyan, :output then 11
|
||||
when :red, :failure then 12
|
||||
when :purple then 13
|
||||
when :yellow then 14
|
||||
when :white then 15
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def posix_colour(colour)
|
||||
# ANSI Escape Codes - Foreground colors
|
||||
# | Code | Color |
|
||||
# | 39 | Default foreground color |
|
||||
# | 30 | Black |
|
||||
# | 31 | Red |
|
||||
# | 32 | Green |
|
||||
# | 33 | Yellow |
|
||||
# | 34 | Blue |
|
||||
# | 35 | Magenta |
|
||||
# | 36 | Cyan |
|
||||
# | 37 | Light gray |
|
||||
# | 90 | Dark gray |
|
||||
# | 91 | Light red |
|
||||
# | 92 | Light green |
|
||||
# | 93 | Light yellow |
|
||||
# | 94 | Light blue |
|
||||
# | 95 | Light magenta |
|
||||
# | 96 | Light cyan |
|
||||
# | 97 | White |
|
||||
|
||||
case colour
|
||||
when :black then 30
|
||||
when :red, :failure then 31
|
||||
when :green, :success then 32
|
||||
when :yellow then 33
|
||||
when :blue, :narrative then 34
|
||||
when :purple, :magenta then 35
|
||||
when :cyan, :output then 36
|
||||
when :white, :default_white then 37
|
||||
when :default then 39
|
||||
else
|
||||
39
|
||||
end
|
||||
end
|
||||
|
||||
def out_c(mode, colour, str)
|
||||
case RUBY_PLATFORM
|
||||
when /(win|w)32$/
|
||||
change_to(colour)
|
||||
$stdout.puts str if mode == :puts
|
||||
$stdout.print str if mode == :print
|
||||
change_to(:default_white)
|
||||
else
|
||||
$stdout.puts("#{change_to(colour)}#{str}\033[0m") if mode == :puts
|
||||
$stdout.print("#{change_to(colour)}#{str}\033[0m") if mode == :print
|
||||
end
|
||||
end
|
||||
end # ColourCommandLine
|
||||
|
||||
def colour_puts(role,str) ColourCommandLine.new.out_c(:puts, role, str) end
|
||||
def colour_print(role,str) ColourCommandLine.new.out_c(:print, role, str) end
|
||||
|
||||
39
tests/unity/auto/colour_reporter.rb
Normal file
39
tests/unity/auto/colour_reporter.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
# ==========================================
|
||||
# Unity Project - A Test Framework for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
require "#{File.expand_path(File.dirname(__FILE__))}/colour_prompt"
|
||||
|
||||
$colour_output = true
|
||||
|
||||
def report(message)
|
||||
if not $colour_output
|
||||
$stdout.puts(message)
|
||||
else
|
||||
message = message.join('\n') if (message.class == Array)
|
||||
message.each_line do |line|
|
||||
line.chomp!
|
||||
colour = case(line)
|
||||
when /(?:total\s+)?tests:?\s+(\d+)\s+(?:total\s+)?failures:?\s+\d+\s+Ignored:?/i
|
||||
($1.to_i == 0) ? :green : :red
|
||||
when /PASS/
|
||||
:green
|
||||
when /^OK$/
|
||||
:green
|
||||
when /(?:FAIL|ERROR)/
|
||||
:red
|
||||
when /IGNORE/
|
||||
:yellow
|
||||
when /^(?:Creating|Compiling|Linking)/
|
||||
:white
|
||||
else
|
||||
:silver
|
||||
end
|
||||
colour_puts(colour, line)
|
||||
end
|
||||
end
|
||||
$stdout.flush
|
||||
$stderr.flush
|
||||
end
|
||||
36
tests/unity/auto/generate_config.yml
Normal file
36
tests/unity/auto/generate_config.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
#this is a sample configuration file for generate_module
|
||||
#you would use it by calling generate_module with the -ygenerate_config.yml option
|
||||
#files like this are useful for customizing generate_module to your environment
|
||||
:generate_module:
|
||||
:defaults:
|
||||
#these defaults are used in place of any missing options at the command line
|
||||
:path_src: ../src/
|
||||
:path_inc: ../src/
|
||||
:path_tst: ../test/
|
||||
:update_svn: true
|
||||
:includes:
|
||||
#use [] for no additional includes, otherwise list the includes on separate lines
|
||||
:src:
|
||||
- Defs.h
|
||||
- Board.h
|
||||
:inc: []
|
||||
:tst:
|
||||
- Defs.h
|
||||
- Board.h
|
||||
- Exception.h
|
||||
:boilerplates:
|
||||
#these are inserted at the top of generated files.
|
||||
#just comment out or remove if not desired.
|
||||
#use %1$s where you would like the file name to appear (path/extension not included)
|
||||
:src: |
|
||||
//-------------------------------------------
|
||||
// %1$s.c
|
||||
//-------------------------------------------
|
||||
:inc: |
|
||||
//-------------------------------------------
|
||||
// %1$s.h
|
||||
//-------------------------------------------
|
||||
:tst: |
|
||||
//-------------------------------------------
|
||||
// Test%1$s.c : Units tests for %1$s.c
|
||||
//-------------------------------------------
|
||||
311
tests/unity/auto/generate_module.rb
Normal file
311
tests/unity/auto/generate_module.rb
Normal file
@@ -0,0 +1,311 @@
|
||||
# ==========================================
|
||||
# Unity Project - A Test Framework for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
# This script creates all the files with start code necessary for a new module.
|
||||
# A simple module only requires a source file, header file, and test file.
|
||||
# Triad modules require a source, header, and test file for each triad type (like model, conductor, and hardware).
|
||||
|
||||
require 'rubygems'
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
|
||||
#TEMPLATE_TST
|
||||
TEMPLATE_TST ||= %q[#include "unity.h"
|
||||
%2$s#include "%1$s.h"
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_%1$s_NeedToImplement(void)
|
||||
{
|
||||
TEST_IGNORE_MESSAGE("Need to Implement %1$s");
|
||||
}
|
||||
]
|
||||
|
||||
#TEMPLATE_SRC
|
||||
TEMPLATE_SRC ||= %q[%2$s#include "%1$s.h"
|
||||
]
|
||||
|
||||
#TEMPLATE_INC
|
||||
TEMPLATE_INC ||= %q[#ifndef _%3$s_H
|
||||
#define _%3$s_H
|
||||
%2$s
|
||||
|
||||
#endif // _%3$s_H
|
||||
]
|
||||
|
||||
class UnityModuleGenerator
|
||||
|
||||
############################
|
||||
def initialize(options=nil)
|
||||
|
||||
here = File.expand_path(File.dirname(__FILE__)) + '/'
|
||||
|
||||
@options = UnityModuleGenerator.default_options
|
||||
case(options)
|
||||
when NilClass then @options
|
||||
when String then @options.merge!(UnityModuleGenerator.grab_config(options))
|
||||
when Hash then @options.merge!(options)
|
||||
else raise "If you specify arguments, it should be a filename or a hash of options"
|
||||
end
|
||||
|
||||
# Create default file paths if none were provided
|
||||
@options[:path_src] = here + "../src/" if @options[:path_src].nil?
|
||||
@options[:path_inc] = @options[:path_src] if @options[:path_inc].nil?
|
||||
@options[:path_tst] = here + "../test/" if @options[:path_tst].nil?
|
||||
@options[:path_src] += '/' unless (@options[:path_src][-1] == 47)
|
||||
@options[:path_inc] += '/' unless (@options[:path_inc][-1] == 47)
|
||||
@options[:path_tst] += '/' unless (@options[:path_tst][-1] == 47)
|
||||
|
||||
#Built in patterns
|
||||
@patterns = { 'src' => {'' => { :inc => [] } },
|
||||
'test'=> {'' => { :inc => [] } },
|
||||
'dh' => {'Driver' => { :inc => [create_filename('%1$s','Hardware.h')] },
|
||||
'Hardware' => { :inc => [] }
|
||||
},
|
||||
'dih' => {'Driver' => { :inc => [create_filename('%1$s','Hardware.h'), create_filename('%1$s','Interrupt.h')] },
|
||||
'Interrupt'=> { :inc => [create_filename('%1$s','Hardware.h')] },
|
||||
'Hardware' => { :inc => [] }
|
||||
},
|
||||
'mch' => {'Model' => { :inc => [] },
|
||||
'Conductor'=> { :inc => [create_filename('%1$s','Model.h'), create_filename('%1$s','Hardware.h')] },
|
||||
'Hardware' => { :inc => [] }
|
||||
},
|
||||
'mvp' => {'Model' => { :inc => [] },
|
||||
'Presenter'=> { :inc => [create_filename('%1$s','Model.h'), create_filename('%1$s','View.h')] },
|
||||
'View' => { :inc => [] }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
############################
|
||||
def self.default_options
|
||||
{
|
||||
:pattern => "src",
|
||||
:includes =>
|
||||
{
|
||||
:src => [],
|
||||
:inc => [],
|
||||
:tst => [],
|
||||
},
|
||||
:update_svn => false,
|
||||
:boilerplates => {},
|
||||
:test_prefix => 'Test',
|
||||
:mock_prefix => 'Mock',
|
||||
}
|
||||
end
|
||||
|
||||
############################
|
||||
def self.grab_config(config_file)
|
||||
options = self.default_options
|
||||
unless (config_file.nil? or config_file.empty?)
|
||||
require 'yaml'
|
||||
yaml_guts = YAML.load_file(config_file)
|
||||
options.merge!(yaml_guts[:unity] || yaml_guts[:cmock])
|
||||
raise "No :unity or :cmock section found in #{config_file}" unless options
|
||||
end
|
||||
return(options)
|
||||
end
|
||||
|
||||
############################
|
||||
def files_to_operate_on(module_name, pattern=nil)
|
||||
#strip any leading path information from the module name and save for later
|
||||
subfolder = File.dirname(module_name)
|
||||
module_name = File.basename(module_name)
|
||||
|
||||
#create triad definition
|
||||
prefix = @options[:test_prefix] || 'Test'
|
||||
triad = [ { :ext => '.c', :path => @options[:path_src], :prefix => "", :template => TEMPLATE_SRC, :inc => :src, :boilerplate => @options[:boilerplates][:src] },
|
||||
{ :ext => '.h', :path => @options[:path_inc], :prefix => "", :template => TEMPLATE_INC, :inc => :inc, :boilerplate => @options[:boilerplates][:inc] },
|
||||
{ :ext => '.c', :path => @options[:path_tst], :prefix => prefix, :template => TEMPLATE_TST, :inc => :tst, :boilerplate => @options[:boilerplates][:tst] },
|
||||
]
|
||||
|
||||
#prepare the pattern for use
|
||||
pattern = (pattern || @options[:pattern] || 'src').downcase
|
||||
patterns = @patterns[pattern]
|
||||
raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil?
|
||||
|
||||
#single file patterns (currently just 'test') can reject the other parts of the triad
|
||||
if (pattern == 'test')
|
||||
triad.reject!{|v| v[:inc] != :tst }
|
||||
end
|
||||
|
||||
# Assemble the path/names of the files we need to work with.
|
||||
files = []
|
||||
triad.each do |cfg|
|
||||
patterns.each_pair do |pattern_file, pattern_traits|
|
||||
submodule_name = create_filename(module_name, pattern_file)
|
||||
filename = cfg[:prefix] + submodule_name + cfg[:ext]
|
||||
files << {
|
||||
:path => (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath,
|
||||
:name => submodule_name,
|
||||
:template => cfg[:template],
|
||||
:boilerplate => cfg[:boilerplate],
|
||||
:includes => case(cfg[:inc])
|
||||
when :src then (@options[:includes][:src] || []) | pattern_traits[:inc].map{|f| f % [module_name]}
|
||||
when :inc then (@options[:includes][:inc] || [])
|
||||
when :tst then (@options[:includes][:tst] || []) | pattern_traits[:inc].map{|f| "#{@options[:mock_prefix]}#{f}" % [module_name]}
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return files
|
||||
end
|
||||
|
||||
############################
|
||||
def create_filename(part1, part2="")
|
||||
if part2.empty?
|
||||
case(@options[:naming])
|
||||
when 'bumpy' then part1
|
||||
when 'camel' then part1
|
||||
when 'snake' then part1.downcase
|
||||
when 'caps' then part1.upcase
|
||||
else part1.downcase
|
||||
end
|
||||
else
|
||||
case(@options[:naming])
|
||||
when 'bumpy' then part1 + part2
|
||||
when 'camel' then part1 + part2
|
||||
when 'snake' then part1.downcase + "_" + part2.downcase
|
||||
when 'caps' then part1.upcase + "_" + part2.upcase
|
||||
else part1.downcase + "_" + part2.downcase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
############################
|
||||
def generate(module_name, pattern=nil)
|
||||
|
||||
files = files_to_operate_on(module_name, pattern)
|
||||
|
||||
#Abort if all of the module files already exist
|
||||
all_files_exist = true
|
||||
files.each do |file|
|
||||
if not File.exist?(file[:path])
|
||||
all_files_exist = false
|
||||
end
|
||||
end
|
||||
raise "ERROR: File #{files[0][:name]} already exists. Exiting." if all_files_exist
|
||||
|
||||
# Create Source Modules
|
||||
files.each_with_index do |file, i|
|
||||
# If this file already exists, don't overwrite it.
|
||||
if File.exist?(file[:path])
|
||||
puts "File #{file[:path]} already exists!"
|
||||
next
|
||||
end
|
||||
# Create the path first if necessary.
|
||||
FileUtils.mkdir_p(File.dirname(file[:path]), :verbose => false)
|
||||
File.open(file[:path], 'w') do |f|
|
||||
f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil?
|
||||
f.write(file[:template] % [ file[:name],
|
||||
file[:includes].map{|f| "#include \"#{f}\"\n"}.join,
|
||||
file[:name].upcase ]
|
||||
)
|
||||
end
|
||||
if (@options[:update_svn])
|
||||
`svn add \"#{file[:path]}\"`
|
||||
if $?.exitstatus == 0
|
||||
puts "File #{file[:path]} created and added to source control"
|
||||
else
|
||||
puts "File #{file[:path]} created but FAILED adding to source control!"
|
||||
end
|
||||
else
|
||||
puts "File #{file[:path]} created"
|
||||
end
|
||||
end
|
||||
puts 'Generate Complete'
|
||||
end
|
||||
|
||||
############################
|
||||
def destroy(module_name, pattern=nil)
|
||||
|
||||
files_to_operate_on(module_name, pattern).each do |filespec|
|
||||
file = filespec[:path]
|
||||
if File.exist?(file)
|
||||
if @options[:update_svn]
|
||||
`svn delete \"#{file}\" --force`
|
||||
puts "File #{file} deleted and removed from source control"
|
||||
else
|
||||
FileUtils.remove(file)
|
||||
puts "File #{file} deleted"
|
||||
end
|
||||
else
|
||||
puts "File #{file} does not exist so cannot be removed."
|
||||
end
|
||||
end
|
||||
puts "Destroy Complete"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
############################
|
||||
#Handle As Command Line If Called That Way
|
||||
if ($0 == __FILE__)
|
||||
destroy = false
|
||||
options = { }
|
||||
module_name = nil
|
||||
|
||||
# Parse the command line parameters.
|
||||
ARGV.each do |arg|
|
||||
case(arg)
|
||||
when /^-d/ then destroy = true
|
||||
when /^-u/ then options[:update_svn] = true
|
||||
when /^-p\"?(\w+)\"?/ then options[:pattern] = $1
|
||||
when /^-s\"?(.+)\"?/ then options[:path_src] = $1
|
||||
when /^-i\"?(.+)\"?/ then options[:path_inc] = $1
|
||||
when /^-t\"?(.+)\"?/ then options[:path_tst] = $1
|
||||
when /^-n\"?(.+)\"?/ then options[:naming] = $1
|
||||
when /^-y\"?(.+)\"?/ then options = UnityModuleGenerator.grab_config($1)
|
||||
when /^(\w+)/
|
||||
raise "ERROR: You can't have more than one Module name specified!" unless module_name.nil?
|
||||
module_name = arg
|
||||
when /^-(h|-help)/
|
||||
ARGV = []
|
||||
else
|
||||
raise "ERROR: Unknown option specified '#{arg}'"
|
||||
end
|
||||
end
|
||||
|
||||
if (!ARGV[0])
|
||||
puts [ "\nGENERATE MODULE\n-------- ------",
|
||||
"\nUsage: ruby generate_module [options] module_name",
|
||||
" -i\"include\" sets the path to output headers to 'include' (DEFAULT ../src)",
|
||||
" -s\"../src\" sets the path to output source to '../src' (DEFAULT ../src)",
|
||||
" -t\"C:/test\" sets the path to output source to 'C:/test' (DEFAULT ../test)",
|
||||
" -p\"MCH\" sets the output pattern to MCH.",
|
||||
" dh - driver hardware.",
|
||||
" dih - driver interrupt hardware.",
|
||||
" mch - model conductor hardware.",
|
||||
" mvp - model view presenter.",
|
||||
" src - just a source module, header and test. (DEFAULT)",
|
||||
" test - just a test file.",
|
||||
" -d destroy module instead of creating it.",
|
||||
" -n\"camel\" sets the file naming convention.",
|
||||
" bumpy - BumpyCaseFilenames.",
|
||||
" camel - camelCaseFilenames.",
|
||||
" snake - snake_case_filenames. (DEFAULT)",
|
||||
" caps - CAPS_CASE_FILENAMES.",
|
||||
" -u update subversion too (requires subversion command line)",
|
||||
" -y\"my.yml\" selects a different yaml config file for module generation",
|
||||
"" ].join("\n")
|
||||
exit
|
||||
end
|
||||
|
||||
raise "ERROR: You must have a Module name specified! (use option -h for help)" if module_name.nil?
|
||||
if (destroy)
|
||||
UnityModuleGenerator.new(options).destroy(module_name)
|
||||
else
|
||||
UnityModuleGenerator.new(options).generate(module_name)
|
||||
end
|
||||
|
||||
end
|
||||
435
tests/unity/auto/generate_test_runner.rb
Normal file
435
tests/unity/auto/generate_test_runner.rb
Normal file
@@ -0,0 +1,435 @@
|
||||
# ==========================================
|
||||
# Unity Project - A Test Framework for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
$QUICK_RUBY_VERSION = RUBY_VERSION.split('.').inject(0){|vv,v| vv * 100 + v.to_i }
|
||||
File.expand_path(File.join(File.dirname(__FILE__),'colour_prompt'))
|
||||
|
||||
class UnityTestRunnerGenerator
|
||||
|
||||
def initialize(options = nil)
|
||||
@options = UnityTestRunnerGenerator.default_options
|
||||
case(options)
|
||||
when NilClass then @options
|
||||
when String then @options.merge!(UnityTestRunnerGenerator.grab_config(options))
|
||||
when Hash then @options.merge!(options)
|
||||
else raise "If you specify arguments, it should be a filename or a hash of options"
|
||||
end
|
||||
require "#{File.expand_path(File.dirname(__FILE__))}/type_sanitizer"
|
||||
end
|
||||
|
||||
def self.default_options
|
||||
{
|
||||
:includes => [],
|
||||
:defines => [],
|
||||
:plugins => [],
|
||||
:framework => :unity,
|
||||
:test_prefix => "test|spec|should",
|
||||
:setup_name => "setUp",
|
||||
:teardown_name => "tearDown",
|
||||
:main_name => "main", #set to :auto to automatically generate each time
|
||||
:main_export_decl => "",
|
||||
:cmdline_args => false,
|
||||
:use_param_tests => false,
|
||||
}
|
||||
end
|
||||
|
||||
def self.grab_config(config_file)
|
||||
options = self.default_options
|
||||
unless (config_file.nil? or config_file.empty?)
|
||||
require 'yaml'
|
||||
yaml_guts = YAML.load_file(config_file)
|
||||
options.merge!(yaml_guts[:unity] || yaml_guts[:cmock])
|
||||
raise "No :unity or :cmock section found in #{config_file}" unless options
|
||||
end
|
||||
return(options)
|
||||
end
|
||||
|
||||
def run(input_file, output_file, options=nil)
|
||||
tests = []
|
||||
testfile_includes = []
|
||||
used_mocks = []
|
||||
|
||||
@options.merge!(options) unless options.nil?
|
||||
module_name = File.basename(input_file)
|
||||
|
||||
#pull required data from source file
|
||||
source = File.read(input_file)
|
||||
source = source.force_encoding("ISO-8859-1").encode("utf-8", :replace => nil) if ($QUICK_RUBY_VERSION > 10900)
|
||||
tests = find_tests(source)
|
||||
headers = find_includes(source)
|
||||
testfile_includes = (headers[:local] + headers[:system])
|
||||
used_mocks = find_mocks(testfile_includes)
|
||||
testfile_includes = (testfile_includes - used_mocks)
|
||||
testfile_includes.delete_if{|inc| inc =~ /(unity|cmock)/}
|
||||
|
||||
#build runner file
|
||||
generate(input_file, output_file, tests, used_mocks, testfile_includes)
|
||||
|
||||
#determine which files were used to return them
|
||||
all_files_used = [input_file, output_file]
|
||||
all_files_used += testfile_includes.map {|filename| filename + '.c'} unless testfile_includes.empty?
|
||||
all_files_used += @options[:includes] unless @options[:includes].empty?
|
||||
return all_files_used.uniq
|
||||
end
|
||||
|
||||
def generate(input_file, output_file, tests, used_mocks, testfile_includes)
|
||||
File.open(output_file, 'w') do |output|
|
||||
create_header(output, used_mocks, testfile_includes)
|
||||
create_externs(output, tests, used_mocks)
|
||||
create_mock_management(output, used_mocks)
|
||||
create_suite_setup_and_teardown(output)
|
||||
create_reset(output, used_mocks)
|
||||
create_main(output, input_file, tests, used_mocks)
|
||||
end
|
||||
|
||||
if (@options[:header_file] && !@options[:header_file].empty?)
|
||||
File.open(@options[:header_file], 'w') do |output|
|
||||
create_h_file(output, @options[:header_file], tests, testfile_includes, used_mocks)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find_tests(source)
|
||||
tests_and_line_numbers = []
|
||||
|
||||
source_scrubbed = source.clone
|
||||
source_scrubbed = source_scrubbed.gsub(/"[^"\n]*"/, '') # remove things in strings
|
||||
source_scrubbed = source_scrubbed.gsub(/\/\/.*$/, '') # remove line comments
|
||||
source_scrubbed = source_scrubbed.gsub(/\/\*.*?\*\//m, '') # remove block comments
|
||||
lines = source_scrubbed.split(/(^\s*\#.*$) # Treat preprocessor directives as a logical line
|
||||
| (;|\{|\}) /x) # Match ;, {, and } as end of lines
|
||||
|
||||
lines.each_with_index do |line, index|
|
||||
#find tests
|
||||
if line =~ /^((?:\s*TEST_CASE\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/
|
||||
arguments = $1
|
||||
name = $2
|
||||
call = $3
|
||||
params = $4
|
||||
args = nil
|
||||
if (@options[:use_param_tests] and !arguments.empty?)
|
||||
args = []
|
||||
arguments.scan(/\s*TEST_CASE\s*\((.*)\)\s*$/) {|a| args << a[0]}
|
||||
end
|
||||
tests_and_line_numbers << { :test => name, :args => args, :call => call, :params => params, :line_number => 0 }
|
||||
end
|
||||
end
|
||||
tests_and_line_numbers.uniq! {|v| v[:test] }
|
||||
|
||||
#determine line numbers and create tests to run
|
||||
source_lines = source.split("\n")
|
||||
source_index = 0;
|
||||
tests_and_line_numbers.size.times do |i|
|
||||
source_lines[source_index..-1].each_with_index do |line, index|
|
||||
if (line =~ /#{tests_and_line_numbers[i][:test]}/)
|
||||
source_index += index
|
||||
tests_and_line_numbers[i][:line_number] = source_index + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return tests_and_line_numbers
|
||||
end
|
||||
|
||||
def find_includes(source)
|
||||
|
||||
#remove comments (block and line, in three steps to ensure correct precedence)
|
||||
source.gsub!(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '') # remove line comments that comment out the start of blocks
|
||||
source.gsub!(/\/\*.*?\*\//m, '') # remove block comments
|
||||
source.gsub!(/\/\/.*$/, '') # remove line comments (all that remain)
|
||||
|
||||
#parse out includes
|
||||
includes = {
|
||||
:local => source.scan(/^\s*#include\s+\"\s*(.+)\.[hH]\s*\"/).flatten,
|
||||
:system => source.scan(/^\s*#include\s+<\s*(.+)\s*>/).flatten.map { |inc| "<#{inc}>" }
|
||||
}
|
||||
return includes
|
||||
end
|
||||
|
||||
def find_mocks(includes)
|
||||
mock_headers = []
|
||||
includes.each do |include_path|
|
||||
include_file = File.basename(include_path)
|
||||
mock_headers << include_path if (include_file =~ /^mock/i)
|
||||
end
|
||||
return mock_headers
|
||||
end
|
||||
|
||||
def create_header(output, mocks, testfile_includes=[])
|
||||
output.puts('/* AUTOGENERATED FILE. DO NOT EDIT. */')
|
||||
create_runtest(output, mocks)
|
||||
output.puts("\n/*=======Automagically Detected Files To Include=====*/")
|
||||
output.puts("#include \"#{@options[:framework].to_s}.h\"")
|
||||
output.puts('#include "cmock.h"') unless (mocks.empty?)
|
||||
output.puts('#include <setjmp.h>')
|
||||
output.puts('#include <stdio.h>')
|
||||
output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception)
|
||||
if (@options[:defines] && !@options[:defines].empty?)
|
||||
@options[:defines].each {|d| output.puts("#define #{d}")}
|
||||
end
|
||||
if (@options[:header_file] && !@options[:header_file].empty?)
|
||||
output.puts("#include \"#{File.basename(@options[:header_file])}\"")
|
||||
else
|
||||
@options[:includes].flatten.uniq.compact.each do |inc|
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h','')}.h\""}")
|
||||
end
|
||||
testfile_includes.each do |inc|
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h','')}.h\""}")
|
||||
end
|
||||
end
|
||||
mocks.each do |mock|
|
||||
output.puts("#include \"#{mock.gsub('.h','')}.h\"")
|
||||
end
|
||||
if @options[:enforce_strict_ordering]
|
||||
output.puts('')
|
||||
output.puts('int GlobalExpectCount;')
|
||||
output.puts('int GlobalVerifyOrder;')
|
||||
output.puts('char* GlobalOrderError;')
|
||||
end
|
||||
end
|
||||
|
||||
def create_externs(output, tests, mocks)
|
||||
output.puts("\n/*=======External Functions This Runner Calls=====*/")
|
||||
output.puts("extern void #{@options[:setup_name]}(void);")
|
||||
output.puts("extern void #{@options[:teardown_name]}(void);")
|
||||
tests.each do |test|
|
||||
output.puts("extern void #{test[:test]}(#{test[:call] || 'void'});")
|
||||
end
|
||||
output.puts('')
|
||||
end
|
||||
|
||||
def create_mock_management(output, mock_headers)
|
||||
unless (mock_headers.empty?)
|
||||
output.puts("\n/*=======Mock Management=====*/")
|
||||
output.puts("static void CMock_Init(void)")
|
||||
output.puts("{")
|
||||
if @options[:enforce_strict_ordering]
|
||||
output.puts(" GlobalExpectCount = 0;")
|
||||
output.puts(" GlobalVerifyOrder = 0;")
|
||||
output.puts(" GlobalOrderError = NULL;")
|
||||
end
|
||||
mocks = mock_headers.map {|mock| File.basename(mock)}
|
||||
mocks.each do |mock|
|
||||
mock_clean = TypeSanitizer.sanitize_c_identifier(mock)
|
||||
output.puts(" #{mock_clean}_Init();")
|
||||
end
|
||||
output.puts("}\n")
|
||||
|
||||
output.puts("static void CMock_Verify(void)")
|
||||
output.puts("{")
|
||||
mocks.each do |mock|
|
||||
mock_clean = TypeSanitizer.sanitize_c_identifier(mock)
|
||||
output.puts(" #{mock_clean}_Verify();")
|
||||
end
|
||||
output.puts("}\n")
|
||||
|
||||
output.puts("static void CMock_Destroy(void)")
|
||||
output.puts("{")
|
||||
mocks.each do |mock|
|
||||
mock_clean = TypeSanitizer.sanitize_c_identifier(mock)
|
||||
output.puts(" #{mock_clean}_Destroy();")
|
||||
end
|
||||
output.puts("}\n")
|
||||
end
|
||||
end
|
||||
|
||||
def create_suite_setup_and_teardown(output)
|
||||
unless (@options[:suite_setup].nil?)
|
||||
output.puts("\n/*=======Suite Setup=====*/")
|
||||
output.puts("static void suite_setup(void)")
|
||||
output.puts("{")
|
||||
output.puts(@options[:suite_setup])
|
||||
output.puts("}")
|
||||
end
|
||||
unless (@options[:suite_teardown].nil?)
|
||||
output.puts("\n/*=======Suite Teardown=====*/")
|
||||
output.puts("static int suite_teardown(int num_failures)")
|
||||
output.puts("{")
|
||||
output.puts(@options[:suite_teardown])
|
||||
output.puts("}")
|
||||
end
|
||||
end
|
||||
|
||||
def create_runtest(output, used_mocks)
|
||||
cexception = @options[:plugins].include? :cexception
|
||||
va_args1 = @options[:use_param_tests] ? ', ...' : ''
|
||||
va_args2 = @options[:use_param_tests] ? '__VA_ARGS__' : ''
|
||||
output.puts("\n/*=======Test Runner Used To Run Each Test Below=====*/")
|
||||
output.puts("#define RUN_TEST_NO_ARGS") if @options[:use_param_tests]
|
||||
output.puts("#define RUN_TEST(TestFunc, TestLineNum#{va_args1}) \\")
|
||||
output.puts("{ \\")
|
||||
output.puts(" Unity.CurrentTestName = #TestFunc#{va_args2.empty? ? '' : " \"(\" ##{va_args2} \")\""}; \\")
|
||||
output.puts(" Unity.CurrentTestLineNumber = TestLineNum; \\")
|
||||
output.puts(" if (UnityTestMatches()) { \\") if (@options[:cmdline_args])
|
||||
output.puts(" Unity.NumberOfTests++; \\")
|
||||
output.puts(" CMock_Init(); \\") unless (used_mocks.empty?)
|
||||
output.puts(" UNITY_CLR_DETAILS(); \\") unless (used_mocks.empty?)
|
||||
output.puts(" if (TEST_PROTECT()) \\")
|
||||
output.puts(" { \\")
|
||||
output.puts(" CEXCEPTION_T e; \\") if cexception
|
||||
output.puts(" Try { \\") if cexception
|
||||
output.puts(" #{@options[:setup_name]}(); \\")
|
||||
output.puts(" TestFunc(#{va_args2}); \\")
|
||||
output.puts(" } Catch(e) { TEST_ASSERT_EQUAL_HEX32_MESSAGE(CEXCEPTION_NONE, e, \"Unhandled Exception!\"); } \\") if cexception
|
||||
output.puts(" } \\")
|
||||
output.puts(" if (TEST_PROTECT()) \\")
|
||||
output.puts(" { \\")
|
||||
output.puts(" #{@options[:teardown_name]}(); \\")
|
||||
output.puts(" CMock_Verify(); \\") unless (used_mocks.empty?)
|
||||
output.puts(" } \\")
|
||||
output.puts(" CMock_Destroy(); \\") unless (used_mocks.empty?)
|
||||
output.puts(" UnityConcludeTest(); \\")
|
||||
output.puts(" } \\") if (@options[:cmdline_args])
|
||||
output.puts("}\n")
|
||||
end
|
||||
|
||||
def create_reset(output, used_mocks)
|
||||
output.puts("\n/*=======Test Reset Option=====*/")
|
||||
output.puts("void resetTest(void);")
|
||||
output.puts("void resetTest(void)")
|
||||
output.puts("{")
|
||||
output.puts(" CMock_Verify();") unless (used_mocks.empty?)
|
||||
output.puts(" CMock_Destroy();") unless (used_mocks.empty?)
|
||||
output.puts(" #{@options[:teardown_name]}();")
|
||||
output.puts(" CMock_Init();") unless (used_mocks.empty?)
|
||||
output.puts(" #{@options[:setup_name]}();")
|
||||
output.puts("}")
|
||||
end
|
||||
|
||||
def create_main(output, filename, tests, used_mocks)
|
||||
output.puts("\n\n/*=======MAIN=====*/")
|
||||
main_name = (@options[:main_name].to_sym == :auto) ? "main_#{filename.gsub('.c','')}" : "#{@options[:main_name]}"
|
||||
if (@options[:cmdline_args])
|
||||
if (main_name != "main")
|
||||
output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv);")
|
||||
end
|
||||
output.puts("#{@options[:main_export_decl]} int #{main_name}(int argc, char** argv)")
|
||||
output.puts("{")
|
||||
output.puts(" int parse_status = UnityParseOptions(argc, argv);")
|
||||
output.puts(" if (parse_status != 0)")
|
||||
output.puts(" {")
|
||||
output.puts(" if (parse_status < 0)")
|
||||
output.puts(" {")
|
||||
output.puts(" UnityPrint(\"#{filename.gsub('.c','')}.\");")
|
||||
output.puts(" UNITY_PRINT_EOL();")
|
||||
if (@options[:use_param_tests])
|
||||
tests.each do |test|
|
||||
if ((test[:args].nil?) or (test[:args].empty?))
|
||||
output.puts(" UnityPrint(\" #{test[:test]}(RUN_TEST_NO_ARGS)\");")
|
||||
output.puts(" UNITY_PRINT_EOL();")
|
||||
else
|
||||
test[:args].each do |args|
|
||||
output.puts(" UnityPrint(\" #{test[:test]}(#{args})\");")
|
||||
output.puts(" UNITY_PRINT_EOL();")
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
tests.each { |test| output.puts(" UnityPrint(\" #{test[:test]}\");\n UNITY_PRINT_EOL();")}
|
||||
end
|
||||
output.puts(" return 0;")
|
||||
output.puts(" }")
|
||||
output.puts(" return parse_status;")
|
||||
output.puts(" }")
|
||||
else
|
||||
if (main_name != "main")
|
||||
output.puts("#{@options[:main_export_decl]} int #{main_name}(void);")
|
||||
end
|
||||
output.puts("int #{main_name}(void)")
|
||||
output.puts("{")
|
||||
end
|
||||
output.puts(" suite_setup();") unless @options[:suite_setup].nil?
|
||||
output.puts(" UnityBegin(\"#{filename.gsub(/\\/,'\\\\\\')}\");")
|
||||
if (@options[:use_param_tests])
|
||||
tests.each do |test|
|
||||
if ((test[:args].nil?) or (test[:args].empty?))
|
||||
output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]}, RUN_TEST_NO_ARGS);")
|
||||
else
|
||||
test[:args].each {|args| output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]}, #{args});")}
|
||||
end
|
||||
end
|
||||
else
|
||||
tests.each { |test| output.puts(" RUN_TEST(#{test[:test]}, #{test[:line_number]});") }
|
||||
end
|
||||
output.puts()
|
||||
output.puts(" CMock_Guts_MemFreeFinal();") unless used_mocks.empty?
|
||||
output.puts(" return #{@options[:suite_teardown].nil? ? "" : "suite_teardown"}(UnityEnd());")
|
||||
output.puts("}")
|
||||
end
|
||||
|
||||
def create_h_file(output, filename, tests, testfile_includes, used_mocks)
|
||||
filename = File.basename(filename).gsub(/[-\/\\\.\,\s]/, "_").upcase
|
||||
output.puts("/* AUTOGENERATED FILE. DO NOT EDIT. */")
|
||||
output.puts("#ifndef _#{filename}")
|
||||
output.puts("#define _#{filename}\n\n")
|
||||
output.puts("#include \"#{@options[:framework].to_s}.h\"")
|
||||
output.puts('#include "cmock.h"') unless (used_mocks.empty?)
|
||||
@options[:includes].flatten.uniq.compact.each do |inc|
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h','')}.h\""}")
|
||||
end
|
||||
testfile_includes.each do |inc|
|
||||
output.puts("#include #{inc.include?('<') ? inc : "\"#{inc.gsub('.h','')}.h\""}")
|
||||
end
|
||||
output.puts "\n"
|
||||
tests.each do |test|
|
||||
if ((test[:params].nil?) or (test[:params].empty?))
|
||||
output.puts("void #{test[:test]}(void);")
|
||||
else
|
||||
output.puts("void #{test[:test]}(#{test[:params]});")
|
||||
end
|
||||
end
|
||||
output.puts("#endif\n\n")
|
||||
end
|
||||
end
|
||||
|
||||
if ($0 == __FILE__)
|
||||
options = { :includes => [] }
|
||||
yaml_file = nil
|
||||
|
||||
#parse out all the options first (these will all be removed as we go)
|
||||
ARGV.reject! do |arg|
|
||||
case(arg)
|
||||
when '-cexception'
|
||||
options[:plugins] = [:cexception]; true
|
||||
when /\.*\.ya?ml/
|
||||
options = UnityTestRunnerGenerator.grab_config(arg); true
|
||||
when /--(\w+)=\"?(.*)\"?/
|
||||
options[$1.to_sym] = $2; true
|
||||
when /\.*\.h/
|
||||
options[:includes] << arg; true
|
||||
else false
|
||||
end
|
||||
end
|
||||
|
||||
#make sure there is at least one parameter left (the input file)
|
||||
if !ARGV[0]
|
||||
puts ["\nusage: ruby #{__FILE__} (files) (options) input_test_file (output)",
|
||||
"\n input_test_file - this is the C file you want to create a runner for",
|
||||
" output - this is the name of the runner file to generate",
|
||||
" defaults to (input_test_file)_Runner",
|
||||
" files:",
|
||||
" *.yml / *.yaml - loads configuration from here in :unity or :cmock",
|
||||
" *.h - header files are added as #includes in runner",
|
||||
" options:",
|
||||
" -cexception - include cexception support",
|
||||
" --setup_name=\"\" - redefine setUp func name to something else",
|
||||
" --teardown_name=\"\" - redefine tearDown func name to something else",
|
||||
" --main_name=\"\" - redefine main func name to something else",
|
||||
" --test_prefix=\"\" - redefine test prefix from default test|spec|should",
|
||||
" --suite_setup=\"\" - code to execute for setup of entire suite",
|
||||
" --suite_teardown=\"\" - code to execute for teardown of entire suite",
|
||||
" --use_param_tests=1 - enable parameterized tests (disabled by default)",
|
||||
" --header_file=\"\" - path/name of test header file to generate too"
|
||||
].join("\n")
|
||||
exit 1
|
||||
end
|
||||
|
||||
#create the default test runner name if not specified
|
||||
ARGV[1] = ARGV[0].gsub(".c","_Runner.c") if (!ARGV[1])
|
||||
|
||||
UnityTestRunnerGenerator.new(options).run(ARGV[0], ARGV[1])
|
||||
end
|
||||
224
tests/unity/auto/parseOutput.rb
Normal file
224
tests/unity/auto/parseOutput.rb
Normal file
@@ -0,0 +1,224 @@
|
||||
#============================================================
|
||||
# Author: John Theofanopoulos
|
||||
# A simple parser. Takes the output files generated during the build process and
|
||||
# extracts information relating to the tests.
|
||||
#
|
||||
# Notes:
|
||||
# To capture an output file under VS builds use the following:
|
||||
# devenv [build instructions] > Output.txt & type Output.txt
|
||||
#
|
||||
# To capture an output file under GCC/Linux builds use the following:
|
||||
# make | tee Output.txt
|
||||
#
|
||||
# To use this parser use the following command
|
||||
# ruby parseOutput.rb [options] [file]
|
||||
# options: -xml : produce a JUnit compatible XML file
|
||||
# file : file to scan for results
|
||||
#============================================================
|
||||
|
||||
|
||||
class ParseOutput
|
||||
# The following flag is set to true when a test is found or false otherwise.
|
||||
@testFlag
|
||||
@xmlOut
|
||||
@arrayList
|
||||
@totalTests
|
||||
@classIndex
|
||||
|
||||
# Set the flag to indicate if there will be an XML output file or not
|
||||
def setXmlOutput()
|
||||
@xmlOut = true
|
||||
end
|
||||
|
||||
# if write our output to XML
|
||||
def writeXmlOuput()
|
||||
output = File.open("report.xml", "w")
|
||||
output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
@arrayList.each do |item|
|
||||
output << item << "\n"
|
||||
end
|
||||
output << "</testsuite>\n"
|
||||
end
|
||||
|
||||
# This function will try and determine when the suite is changed. This is
|
||||
# is the name that gets added to the classname parameter.
|
||||
def testSuiteVerify(testSuiteName)
|
||||
if @testFlag == false
|
||||
@testFlag = true;
|
||||
# Split the path name
|
||||
testName = testSuiteName.split("/")
|
||||
# Remove the extension
|
||||
baseName = testName[testName.size - 1].split(".")
|
||||
@testSuite = "test." + baseName[0]
|
||||
printf "New Test: %s\n", @testSuite
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Test was flagged as having passed so format the output
|
||||
def testPassed(array)
|
||||
lastItem = array.length - 1
|
||||
testName = array[lastItem - 1]
|
||||
testSuiteVerify(array[@className])
|
||||
printf "%-40s PASS\n", testName
|
||||
if @xmlOut == true
|
||||
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\"/>"
|
||||
end
|
||||
end
|
||||
|
||||
# Test was flagged as having passed so format the output.
|
||||
# This is using the Unity fixture output and not the original Unity output.
|
||||
def testPassedUnityFixture(array)
|
||||
testSuite = array[0].sub("TEST(", "")
|
||||
testSuite = testSuite.sub(",", "")
|
||||
testName = array[1].sub(")", "")
|
||||
if @xmlOut == true
|
||||
@arrayList.push " <testcase classname=\"" + testSuite + "\" name=\"" + testName + "\"/>"
|
||||
end
|
||||
end
|
||||
|
||||
# Test was flagged as being ingored so format the output
|
||||
def testIgnored(array)
|
||||
lastItem = array.length - 1
|
||||
testName = array[lastItem - 2]
|
||||
reason = array[lastItem].chomp
|
||||
testSuiteVerify(array[@className])
|
||||
printf "%-40s IGNORED\n", testName
|
||||
|
||||
if testName.start_with? "TEST("
|
||||
array2 = testName.split(" ")
|
||||
@testSuite = array2[0].sub("TEST(", "")
|
||||
@testSuite = @testSuite.sub(",", "")
|
||||
testName = array2[1].sub(")", "")
|
||||
end
|
||||
|
||||
if @xmlOut == true
|
||||
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
|
||||
@arrayList.push " <skipped type=\"TEST IGNORED\"> " + reason + " </skipped>"
|
||||
@arrayList.push " </testcase>"
|
||||
end
|
||||
end
|
||||
|
||||
# Test was flagged as having failed so format the line
|
||||
def testFailed(array)
|
||||
lastItem = array.length - 1
|
||||
testName = array[lastItem - 2]
|
||||
reason = array[lastItem].chomp + " at line: " + array[lastItem - 3]
|
||||
testSuiteVerify(array[@className])
|
||||
printf "%-40s FAILED\n", testName
|
||||
|
||||
if testName.start_with? "TEST("
|
||||
array2 = testName.split(" ")
|
||||
@testSuite = array2[0].sub("TEST(", "")
|
||||
@testSuite = @testSuite.sub(",", "")
|
||||
testName = array2[1].sub(")", "")
|
||||
end
|
||||
|
||||
if @xmlOut == true
|
||||
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
|
||||
@arrayList.push " <failure type=\"ASSERT FAILED\"> " + reason + " </failure>"
|
||||
@arrayList.push " </testcase>"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Figure out what OS we are running on. For now we are assuming if it's not Windows it must
|
||||
# be Unix based.
|
||||
def detectOS()
|
||||
myOS = RUBY_PLATFORM.split("-")
|
||||
if myOS.size == 2
|
||||
if myOS[1] == "mingw32"
|
||||
@className = 1
|
||||
else
|
||||
@className = 0
|
||||
end
|
||||
else
|
||||
@className = 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Main function used to parse the file that was captured.
|
||||
def process(name)
|
||||
@testFlag = false
|
||||
@arrayList = Array.new
|
||||
|
||||
detectOS()
|
||||
|
||||
puts "Parsing file: " + name
|
||||
|
||||
|
||||
testPass = 0
|
||||
testFail = 0
|
||||
testIgnore = 0
|
||||
puts ""
|
||||
puts "=================== RESULTS ====================="
|
||||
puts ""
|
||||
File.open(name).each do |line|
|
||||
# Typical test lines look like this:
|
||||
# <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0
|
||||
# <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented
|
||||
# <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS
|
||||
#
|
||||
# where path is different on Unix vs Windows devices (Windows leads with a drive letter)
|
||||
lineArray = line.split(":")
|
||||
lineSize = lineArray.size
|
||||
# If we were able to split the line then we can look to see if any of our target words
|
||||
# were found. Case is important.
|
||||
if ((lineSize >= 4) || (line.start_with? "TEST("))
|
||||
# Determine if this test passed
|
||||
if line.include? ":PASS"
|
||||
testPassed(lineArray)
|
||||
testPass += 1
|
||||
elsif line.include? ":FAIL:"
|
||||
testFailed(lineArray)
|
||||
testFail += 1
|
||||
elsif line.include? ":IGNORE:"
|
||||
testIgnored(lineArray)
|
||||
testIgnore += 1
|
||||
elsif line.start_with? "TEST("
|
||||
if line.include? " PASS"
|
||||
lineArray = line.split(" ")
|
||||
testPassedUnityFixture(lineArray)
|
||||
testPass += 1
|
||||
end
|
||||
# If none of the keywords are found there are no more tests for this suite so clear
|
||||
# the test flag
|
||||
else
|
||||
@testFlag = false
|
||||
end
|
||||
else
|
||||
@testFlag = false
|
||||
end
|
||||
end
|
||||
puts ""
|
||||
puts "=================== SUMMARY ====================="
|
||||
puts ""
|
||||
puts "Tests Passed : " + testPass.to_s
|
||||
puts "Tests Failed : " + testFail.to_s
|
||||
puts "Tests Ignored : " + testIgnore.to_s
|
||||
@totalTests = testPass + testFail + testIgnore
|
||||
if @xmlOut == true
|
||||
heading = "<testsuite tests=\"" + @totalTests.to_s + "\" failures=\"" + testFail.to_s + "\"" + " skips=\"" + testIgnore.to_s + "\">"
|
||||
@arrayList.insert(0, heading)
|
||||
writeXmlOuput()
|
||||
end
|
||||
|
||||
# return result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# If the command line has no values in, used a default value of Output.txt
|
||||
parseMyFile = ParseOutput.new
|
||||
|
||||
if ARGV.size >= 1
|
||||
ARGV.each do |a|
|
||||
if a == "-xml"
|
||||
parseMyFile.setXmlOutput();
|
||||
else
|
||||
parseMyFile.process(a)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
264
tests/unity/auto/stylize_as_junit.rb
Normal file
264
tests/unity/auto/stylize_as_junit.rb
Normal file
@@ -0,0 +1,264 @@
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# unity_to_junit.rb
|
||||
#
|
||||
require 'fileutils'
|
||||
require 'optparse'
|
||||
require 'ostruct'
|
||||
require 'set'
|
||||
|
||||
require 'pp'
|
||||
|
||||
VERSION = 1.0
|
||||
|
||||
class ArgvParser
|
||||
|
||||
#
|
||||
# Return a structure describing the options.
|
||||
#
|
||||
def self.parse(args)
|
||||
# The options specified on the command line will be collected in *options*.
|
||||
# We set default values here.
|
||||
options = OpenStruct.new
|
||||
options.results_dir = "."
|
||||
options.root_path = "."
|
||||
options.out_file = "results.xml"
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = "Usage: unity_to_junit.rb [options]"
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Specific options:"
|
||||
|
||||
opts.on("-r", "--results <dir>", "Look for Unity Results files here.") do |results|
|
||||
#puts "results #{results}"
|
||||
options.results_dir = results
|
||||
end
|
||||
|
||||
opts.on("-p", "--root_path <path>", "Prepend this path to files in results.") do |root_path|
|
||||
options.root_path = root_path
|
||||
end
|
||||
|
||||
opts.on("-o", "--output <filename>", "XML file to generate.") do |out_file|
|
||||
#puts "out_file: #{out_file}"
|
||||
options.out_file = out_file
|
||||
end
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Common options:"
|
||||
|
||||
# No argument, shows at tail. This will print an options summary.
|
||||
opts.on_tail("-h", "--help", "Show this message") do
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
|
||||
# Another typical switch to print the version.
|
||||
opts.on_tail("--version", "Show version") do
|
||||
puts "unity_to_junit.rb version #{VERSION}"
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
opts.parse!(args)
|
||||
options
|
||||
end # parse()
|
||||
|
||||
end # class OptparseExample
|
||||
|
||||
class UnityToJUnit
|
||||
include FileUtils::Verbose
|
||||
attr_reader :report, :total_tests, :failures, :ignored
|
||||
|
||||
def initialize
|
||||
@report = ''
|
||||
@unit_name = ''
|
||||
end
|
||||
|
||||
def run
|
||||
# Clean up result file names
|
||||
results = @targets.map {|target| target.gsub(/\\/,"/")}
|
||||
#puts "Output File: #{@out_file}"
|
||||
f = File.new(@out_file, "w")
|
||||
write_xml_header(f)
|
||||
write_suites_header( f )
|
||||
results.each do |result_file|
|
||||
lines = File.readlines(result_file).map { |line| line.chomp }
|
||||
if lines.length == 0
|
||||
raise "Empty test result file: #{result_file}"
|
||||
else
|
||||
result_output = get_details(result_file, lines)
|
||||
tests,failures,ignored = parse_test_summary(lines)
|
||||
result_output[:counts][:total] = tests
|
||||
result_output[:counts][:failed] = failures
|
||||
result_output[:counts][:ignored] = ignored
|
||||
result_output[:counts][:passed] = (result_output[:counts][:total] - result_output[:counts][:failed] - result_output[:counts][:ignored])
|
||||
end
|
||||
#use line[0] from the test output to get the test_file path and name
|
||||
test_file_str = lines[0].gsub("\\","/")
|
||||
test_file_str = test_file_str.split(":")
|
||||
test_file = if (test_file_str.length < 2)
|
||||
result_file
|
||||
else
|
||||
test_file_str[0] + ':' + test_file_str[1]
|
||||
end
|
||||
result_output[:source][:path] = File.dirname(test_file)
|
||||
result_output[:source][:file] = File.basename(test_file)
|
||||
|
||||
# save result_output
|
||||
@unit_name = File.basename(test_file, ".*")
|
||||
|
||||
write_suite_header( result_output[:counts], f)
|
||||
write_failures( result_output, f )
|
||||
write_tests( result_output, f )
|
||||
write_ignored( result_output, f )
|
||||
write_suite_footer( f )
|
||||
end
|
||||
write_suites_footer( f )
|
||||
f.close
|
||||
end
|
||||
|
||||
def set_targets(target_array)
|
||||
@targets = target_array
|
||||
end
|
||||
|
||||
def set_root_path(path)
|
||||
@root = path
|
||||
end
|
||||
def set_out_file(filename)
|
||||
@out_file = filename
|
||||
end
|
||||
def usage(err_msg=nil)
|
||||
puts "\nERROR: "
|
||||
puts err_msg if err_msg
|
||||
puts "Usage: unity_to_junit.rb [options]"
|
||||
puts ""
|
||||
puts "Specific options:"
|
||||
puts " -r, --results <dir> Look for Unity Results files here."
|
||||
puts " -p, --root_path <path> Prepend this path to files in results."
|
||||
puts " -o, --output <filename> XML file to generate."
|
||||
puts ""
|
||||
puts "Common options:"
|
||||
puts " -h, --help Show this message"
|
||||
puts " --version Show version"
|
||||
|
||||
exit 1
|
||||
end
|
||||
|
||||
protected
|
||||
def get_details(result_file, lines)
|
||||
results = get_results_structure
|
||||
lines.each do |line|
|
||||
line = line.gsub("\\","/")
|
||||
src_file,src_line,test_name,status,msg = line.split(/:/)
|
||||
line_out = ((@root and (@root != 0)) ? "#{@root}#{line}" : line ).gsub(/\//, "\\")
|
||||
case(status)
|
||||
when 'IGNORE' then results[:ignores] << {:test => test_name, :line => src_line, :message => msg}
|
||||
when 'FAIL' then results[:failures] << {:test => test_name, :line => src_line, :message => msg}
|
||||
when 'PASS' then results[:successes] << {:test => test_name, :line => src_line, :message => msg}
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
def parse_test_summary(summary)
|
||||
if summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ }
|
||||
[$1.to_i,$2.to_i,$3.to_i]
|
||||
else
|
||||
raise "Couldn't parse test results: #{summary}"
|
||||
end
|
||||
end
|
||||
def here; File.expand_path(File.dirname(__FILE__)); end
|
||||
|
||||
private
|
||||
|
||||
def get_results_structure
|
||||
return {
|
||||
:source => {:path => '', :file => ''},
|
||||
:successes => [],
|
||||
:failures => [],
|
||||
:ignores => [],
|
||||
:counts => {:total => 0, :passed => 0, :failed => 0, :ignored => 0},
|
||||
:stdout => [],
|
||||
}
|
||||
end
|
||||
|
||||
def write_xml_header( stream )
|
||||
stream.puts "<?xml version='1.0' encoding='utf-8' ?>"
|
||||
end
|
||||
|
||||
def write_suites_header( stream )
|
||||
stream.puts "<testsuites>"
|
||||
end
|
||||
|
||||
def write_suite_header( counts, stream )
|
||||
stream.puts "\t<testsuite errors=\"0\" skipped=\"#{counts[:ignored]}\" failures=\"#{counts[:failed]}\" tests=\"#{counts[:total]}\" name=\"unity\">"
|
||||
end
|
||||
|
||||
def write_failures( results, stream )
|
||||
result = results[:failures]
|
||||
result.each do |item|
|
||||
filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
|
||||
stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
|
||||
stream.puts "\t\t\t<failure message=\"#{item[:message]}\" type=\"Assertion\"/>"
|
||||
stream.puts "\t\t\t<system-err>
[File] #{filename}
[Line] #{item[:line]}
</system-err>"
|
||||
stream.puts "\t\t</testcase>"
|
||||
end
|
||||
end
|
||||
|
||||
def write_tests( results, stream )
|
||||
result = results[:successes]
|
||||
result.each do |item|
|
||||
filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
|
||||
stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\" />"
|
||||
end
|
||||
end
|
||||
|
||||
def write_ignored( results, stream )
|
||||
result = results[:ignores]
|
||||
result.each do |item|
|
||||
filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
|
||||
puts "Writing ignored tests for test harness: #{filename}"
|
||||
stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
|
||||
stream.puts "\t\t\t<skipped message=\"#{item[:message]}\" type=\"Assertion\"/>"
|
||||
stream.puts "\t\t\t<system-err>
[File] #{filename}
[Line] #{item[:line]}
</system-err>"
|
||||
stream.puts "\t\t</testcase>"
|
||||
end
|
||||
end
|
||||
|
||||
def write_suite_footer( stream )
|
||||
stream.puts "\t</testsuite>"
|
||||
end
|
||||
|
||||
def write_suites_footer( stream )
|
||||
stream.puts "</testsuites>"
|
||||
end
|
||||
end #UnityToJUnit
|
||||
|
||||
if __FILE__ == $0
|
||||
#parse out the command options
|
||||
options = ArgvParser.parse(ARGV)
|
||||
|
||||
#create an instance to work with
|
||||
utj = UnityToJUnit.new
|
||||
begin
|
||||
#look in the specified or current directory for result files
|
||||
targets = "#{options.results_dir.gsub(/\\/, '/')}**/*.test*"
|
||||
|
||||
results = Dir[targets]
|
||||
raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty?
|
||||
utj.set_targets(results)
|
||||
|
||||
#set the root path
|
||||
utj.set_root_path(options.root_path)
|
||||
|
||||
#set the output XML file name
|
||||
#puts "Output File from options: #{options.out_file}"
|
||||
utj.set_out_file(options.out_file)
|
||||
|
||||
#run the summarizer
|
||||
puts utj.run
|
||||
rescue Exception => e
|
||||
utj.usage e.message
|
||||
end
|
||||
end
|
||||
23
tests/unity/auto/test_file_filter.rb
Normal file
23
tests/unity/auto/test_file_filter.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
# ==========================================
|
||||
# Unity Project - A Test Framework for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
require'yaml'
|
||||
|
||||
module RakefileHelpers
|
||||
class TestFileFilter
|
||||
def initialize(all_files = false)
|
||||
@all_files = all_files
|
||||
if not @all_files == true
|
||||
if File.exist?('test_file_filter.yml')
|
||||
filters = YAML.load_file( 'test_file_filter.yml' )
|
||||
@all_files, @only_files, @exclude_files =
|
||||
filters[:all_files], filters[:only_files], filters[:exclude_files]
|
||||
end
|
||||
end
|
||||
end
|
||||
attr_accessor :all_files, :only_files, :exclude_files
|
||||
end
|
||||
end
|
||||
8
tests/unity/auto/type_sanitizer.rb
Normal file
8
tests/unity/auto/type_sanitizer.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
module TypeSanitizer
|
||||
|
||||
def self.sanitize_c_identifier(unsanitized)
|
||||
# convert filename to valid C identifier by replacing invalid chars with '_'
|
||||
return unsanitized.gsub(/[-\/\\\.\,\s]/, "_")
|
||||
end
|
||||
|
||||
end
|
||||
139
tests/unity/auto/unity_test_summary.py
Normal file
139
tests/unity/auto/unity_test_summary.py
Normal file
@@ -0,0 +1,139 @@
|
||||
#! python3
|
||||
# ==========================================
|
||||
# Unity Project - A Test Framework for C
|
||||
# Copyright (c) 2015 Alexander Mueller / XelaRellum@web.de
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# Based on the ruby script by Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# ==========================================
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
from glob import glob
|
||||
|
||||
class UnityTestSummary:
|
||||
def __init__(self):
|
||||
self.report = ''
|
||||
self.total_tests = 0
|
||||
self.failures = 0
|
||||
self.ignored = 0
|
||||
|
||||
def run(self):
|
||||
# Clean up result file names
|
||||
results = []
|
||||
for target in self.targets:
|
||||
results.append(target.replace('\\', '/'))
|
||||
|
||||
# Dig through each result file, looking for details on pass/fail:
|
||||
failure_output = []
|
||||
ignore_output = []
|
||||
|
||||
for result_file in results:
|
||||
lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n')))
|
||||
if len(lines) == 0:
|
||||
raise Exception("Empty test result file: %s" % result_file)
|
||||
|
||||
details = self.get_details(result_file, lines)
|
||||
failures = details['failures']
|
||||
ignores = details['ignores']
|
||||
if len(failures) > 0: failure_output.append('\n'.join(failures))
|
||||
if len(ignores) > 0: ignore_output.append('n'.join(ignores))
|
||||
tests,failures,ignored = self.parse_test_summary('\n'.join(lines))
|
||||
self.total_tests += tests
|
||||
self.failures += failures
|
||||
self.ignored += ignored
|
||||
|
||||
if self.ignored > 0:
|
||||
self.report += "\n"
|
||||
self.report += "--------------------------\n"
|
||||
self.report += "UNITY IGNORED TEST SUMMARY\n"
|
||||
self.report += "--------------------------\n"
|
||||
self.report += "\n".join(ignore_output)
|
||||
|
||||
if self.failures > 0:
|
||||
self.report += "\n"
|
||||
self.report += "--------------------------\n"
|
||||
self.report += "UNITY FAILED TEST SUMMARY\n"
|
||||
self.report += "--------------------------\n"
|
||||
self.report += '\n'.join(failure_output)
|
||||
|
||||
self.report += "\n"
|
||||
self.report += "--------------------------\n"
|
||||
self.report += "OVERALL UNITY TEST SUMMARY\n"
|
||||
self.report += "--------------------------\n"
|
||||
self.report += "{total_tests} TOTAL TESTS {failures} TOTAL FAILURES {ignored} IGNORED\n".format(total_tests = self.total_tests, failures=self.failures, ignored=self.ignored)
|
||||
self.report += "\n"
|
||||
|
||||
return self.report
|
||||
|
||||
def set_targets(self, target_array):
|
||||
self.targets = target_array
|
||||
|
||||
def set_root_path(self, path):
|
||||
self.root = path
|
||||
|
||||
def usage(self, err_msg=None):
|
||||
print("\nERROR: ")
|
||||
if err_msg:
|
||||
print(err_msg)
|
||||
print("\nUsage: unity_test_summary.py result_file_directory/ root_path/")
|
||||
print(" result_file_directory - The location of your results files.")
|
||||
print(" Defaults to current directory if not specified.")
|
||||
print(" Should end in / if specified.")
|
||||
print(" root_path - Helpful for producing more verbose output if using relative paths.")
|
||||
sys.exit(1)
|
||||
|
||||
def get_details(self, result_file, lines):
|
||||
results = { 'failures': [], 'ignores': [], 'successes': [] }
|
||||
for line in lines:
|
||||
parts = line.split(':')
|
||||
if len(parts) == 5:
|
||||
src_file,src_line,test_name,status,msg = parts
|
||||
elif len(parts) == 4:
|
||||
src_file,src_line,test_name,status = parts
|
||||
msg = ''
|
||||
else:
|
||||
continue
|
||||
if len(self.root) > 0:
|
||||
line_out = "%s%s" % (self.root, line)
|
||||
else:
|
||||
line_out = line
|
||||
if status == 'IGNORE':
|
||||
results['ignores'].append(line_out)
|
||||
elif status == 'FAIL':
|
||||
results['failures'].append(line_out)
|
||||
elif status == 'PASS':
|
||||
results['successes'].append(line_out)
|
||||
return results
|
||||
|
||||
def parse_test_summary(self, summary):
|
||||
m = re.search(r"([0-9]+) Tests ([0-9]+) Failures ([0-9]+) Ignored", summary)
|
||||
if not m:
|
||||
raise Exception("Couldn't parse test results: %s" % summary)
|
||||
|
||||
return int(m.group(1)), int(m.group(2)), int(m.group(3))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
uts = UnityTestSummary()
|
||||
try:
|
||||
#look in the specified or current directory for result files
|
||||
if len(sys.argv) > 1:
|
||||
targets_dir = sys.argv[1]
|
||||
else:
|
||||
targets_dir = './'
|
||||
targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '*.test*')))
|
||||
if len(targets) == 0:
|
||||
raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir)
|
||||
uts.set_targets(targets)
|
||||
|
||||
#set the root path
|
||||
if len(sys.argv) > 2:
|
||||
root_path = sys.argv[2]
|
||||
else:
|
||||
root_path = os.path.split(__file__)[0]
|
||||
uts.set_root_path(root_path)
|
||||
|
||||
#run the summarizer
|
||||
print(uts.run())
|
||||
except Exception as e:
|
||||
uts.usage(e)
|
||||
148
tests/unity/auto/unity_test_summary.rb
Normal file
148
tests/unity/auto/unity_test_summary.rb
Normal file
@@ -0,0 +1,148 @@
|
||||
# ==========================================
|
||||
# Unity Project - A Test Framework for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
#!/usr/bin/ruby
|
||||
#
|
||||
# unity_test_summary.rb
|
||||
#
|
||||
require 'fileutils'
|
||||
require 'set'
|
||||
|
||||
class UnityTestSummary
|
||||
include FileUtils::Verbose
|
||||
|
||||
attr_reader :report, :total_tests, :failures, :ignored
|
||||
|
||||
def initialize(opts = {})
|
||||
@report = ''
|
||||
@total_tests = 0
|
||||
@failures = 0
|
||||
@ignored = 0
|
||||
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
# Clean up result file names
|
||||
results = @targets.map {|target| target.gsub(/\\/,'/')}
|
||||
|
||||
# Dig through each result file, looking for details on pass/fail:
|
||||
failure_output = []
|
||||
ignore_output = []
|
||||
|
||||
results.each do |result_file|
|
||||
lines = File.readlines(result_file).map { |line| line.chomp }
|
||||
if lines.length == 0
|
||||
raise "Empty test result file: #{result_file}"
|
||||
else
|
||||
output = get_details(result_file, lines)
|
||||
failure_output << output[:failures] unless output[:failures].empty?
|
||||
ignore_output << output[:ignores] unless output[:ignores].empty?
|
||||
tests,failures,ignored = parse_test_summary(lines)
|
||||
@total_tests += tests
|
||||
@failures += failures
|
||||
@ignored += ignored
|
||||
end
|
||||
end
|
||||
|
||||
if @ignored > 0
|
||||
@report += "\n"
|
||||
@report += "--------------------------\n"
|
||||
@report += "UNITY IGNORED TEST SUMMARY\n"
|
||||
@report += "--------------------------\n"
|
||||
@report += ignore_output.flatten.join("\n")
|
||||
end
|
||||
|
||||
if @failures > 0
|
||||
@report += "\n"
|
||||
@report += "--------------------------\n"
|
||||
@report += "UNITY FAILED TEST SUMMARY\n"
|
||||
@report += "--------------------------\n"
|
||||
@report += failure_output.flatten.join("\n")
|
||||
end
|
||||
|
||||
@report += "\n"
|
||||
@report += "--------------------------\n"
|
||||
@report += "OVERALL UNITY TEST SUMMARY\n"
|
||||
@report += "--------------------------\n"
|
||||
@report += "#{@total_tests} TOTAL TESTS #{@failures} TOTAL FAILURES #{@ignored} IGNORED\n"
|
||||
@report += "\n"
|
||||
end
|
||||
|
||||
def set_targets(target_array)
|
||||
@targets = target_array
|
||||
end
|
||||
|
||||
def set_root_path(path)
|
||||
@root = path
|
||||
end
|
||||
|
||||
def usage(err_msg=nil)
|
||||
puts "\nERROR: "
|
||||
puts err_msg if err_msg
|
||||
puts "\nUsage: unity_test_summary.rb result_file_directory/ root_path/"
|
||||
puts " result_file_directory - The location of your results files."
|
||||
puts " Defaults to current directory if not specified."
|
||||
puts " Should end in / if specified."
|
||||
puts " root_path - Helpful for producing more verbose output if using relative paths."
|
||||
exit 1
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def get_details(result_file, lines)
|
||||
results = { :failures => [], :ignores => [], :successes => [] }
|
||||
lines.each do |line|
|
||||
src_file,src_line,test_name,status,msg = line.split(/:/)
|
||||
line_out = ((@root && (@root != 0)) ? "#{@root}#{line}" : line ).gsub(/\//, "\\")
|
||||
case(status)
|
||||
when 'IGNORE' then results[:ignores] << line_out
|
||||
when 'FAIL' then results[:failures] << line_out
|
||||
when 'PASS' then results[:successes] << line_out
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
def parse_test_summary(summary)
|
||||
if summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ }
|
||||
[$1.to_i,$2.to_i,$3.to_i]
|
||||
else
|
||||
raise "Couldn't parse test results: #{summary}"
|
||||
end
|
||||
end
|
||||
|
||||
def here; File.expand_path(File.dirname(__FILE__)); end
|
||||
|
||||
end
|
||||
|
||||
if $0 == __FILE__
|
||||
|
||||
#parse out the command options
|
||||
opts, args = ARGV.partition {|v| v =~ /^--\w+/}
|
||||
opts.map! {|v| v[2..-1].to_sym }
|
||||
|
||||
#create an instance to work with
|
||||
uts = UnityTestSummary.new(opts)
|
||||
|
||||
begin
|
||||
#look in the specified or current directory for result files
|
||||
args[0] ||= './'
|
||||
targets = "#{ARGV[0].gsub(/\\/, '/')}**/*.test*"
|
||||
results = Dir[targets]
|
||||
raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty?
|
||||
uts.set_targets(results)
|
||||
|
||||
#set the root path
|
||||
args[1] ||= Dir.pwd + '/'
|
||||
uts.set_root_path(ARGV[1])
|
||||
|
||||
#run the summarizer
|
||||
puts uts.run
|
||||
rescue Exception => e
|
||||
uts.usage e.message
|
||||
end
|
||||
end
|
||||
146
tests/unity/auto/unity_to_junit.py
Normal file
146
tests/unity/auto/unity_to_junit.py
Normal file
@@ -0,0 +1,146 @@
|
||||
import sys
|
||||
import os
|
||||
from glob import glob
|
||||
|
||||
from pyparsing import *
|
||||
from junit_xml import TestSuite, TestCase
|
||||
|
||||
|
||||
class UnityTestSummary:
|
||||
def __init__(self):
|
||||
self.report = ''
|
||||
self.total_tests = 0
|
||||
self.failures = 0
|
||||
self.ignored = 0
|
||||
self.targets = 0
|
||||
self.root = None
|
||||
self.test_suites = dict()
|
||||
|
||||
def run(self):
|
||||
# Clean up result file names
|
||||
results = []
|
||||
for target in self.targets:
|
||||
results.append(target.replace('\\', '/'))
|
||||
|
||||
# Dig through each result file, looking for details on pass/fail:
|
||||
for result_file in results:
|
||||
lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n')))
|
||||
if len(lines) == 0:
|
||||
raise Exception("Empty test result file: %s" % result_file)
|
||||
|
||||
# define an expression for your file reference
|
||||
entry_one = Combine(
|
||||
oneOf(list(alphas)) + ':/' +
|
||||
Word(alphanums + '_-./'))
|
||||
|
||||
entry_two = Word(printables + ' ', excludeChars=':')
|
||||
entry = entry_one | entry_two
|
||||
|
||||
delimiter = Literal(':').suppress()
|
||||
tc_result_line = Group(entry.setResultsName('tc_file_name') + delimiter + entry.setResultsName(
|
||||
'tc_line_nr') + delimiter + entry.setResultsName('tc_name') + delimiter + entry.setResultsName(
|
||||
'tc_status') + Optional(
|
||||
delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line")
|
||||
|
||||
eol = LineEnd().suppress()
|
||||
sol = LineStart().suppress()
|
||||
blank_line = sol + eol
|
||||
|
||||
tc_summary_line = Group(Word(nums).setResultsName("num_of_tests") + "Tests" + Word(nums).setResultsName(
|
||||
"num_of_fail") + "Failures" + Word(nums).setResultsName("num_of_ignore") + "Ignored").setResultsName(
|
||||
"tc_summary")
|
||||
tc_end_line = Or(Literal("FAIL"), Literal('Ok')).setResultsName("tc_result")
|
||||
|
||||
# run it and see...
|
||||
pp1 = tc_result_line | Optional(tc_summary_line | tc_end_line)
|
||||
pp1.ignore(blank_line | OneOrMore("-"))
|
||||
|
||||
result = list()
|
||||
for l in lines:
|
||||
result.append((pp1.parseString(l)).asDict())
|
||||
# delete empty results
|
||||
result = filter(None, result)
|
||||
|
||||
tc_list = list()
|
||||
for r in result:
|
||||
if 'tc_line' in r:
|
||||
tmp_tc_line = r['tc_line']
|
||||
|
||||
# get only the file name which will be used as the classname
|
||||
file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0]
|
||||
tmp_tc = TestCase(name=tmp_tc_line['tc_name'], classname=file_name)
|
||||
if 'tc_status' in tmp_tc_line:
|
||||
if str(tmp_tc_line['tc_status']) == 'IGNORE':
|
||||
if 'tc_msg' in tmp_tc_line:
|
||||
tmp_tc.add_skipped_info(message=tmp_tc_line['tc_msg'],
|
||||
output=r'[File]={0}, [Line]={1}'.format(
|
||||
tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr']))
|
||||
else:
|
||||
tmp_tc.add_skipped_info(message=" ")
|
||||
elif str(tmp_tc_line['tc_status']) == 'FAIL':
|
||||
if 'tc_msg' in tmp_tc_line:
|
||||
tmp_tc.add_failure_info(message=tmp_tc_line['tc_msg'],
|
||||
output=r'[File]={0}, [Line]={1}'.format(
|
||||
tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr']))
|
||||
else:
|
||||
tmp_tc.add_failure_info(message=" ")
|
||||
|
||||
tc_list.append((str(result_file), tmp_tc))
|
||||
|
||||
for k, v in tc_list:
|
||||
try:
|
||||
self.test_suites[k].append(v)
|
||||
except KeyError:
|
||||
self.test_suites[k] = [v]
|
||||
ts = []
|
||||
for suite_name in self.test_suites:
|
||||
ts.append(TestSuite(suite_name, self.test_suites[suite_name]))
|
||||
|
||||
with open('result.xml', 'w') as f:
|
||||
TestSuite.to_file(f, ts, prettyprint='True', encoding='utf-8')
|
||||
|
||||
return self.report
|
||||
|
||||
def set_targets(self, target_array):
|
||||
self.targets = target_array
|
||||
|
||||
def set_root_path(self, path):
|
||||
self.root = path
|
||||
|
||||
@staticmethod
|
||||
def usage(err_msg=None):
|
||||
print("\nERROR: ")
|
||||
if err_msg:
|
||||
print(err_msg)
|
||||
print("\nUsage: unity_test_summary.py result_file_directory/ root_path/")
|
||||
print(" result_file_directory - The location of your results files.")
|
||||
print(" Defaults to current directory if not specified.")
|
||||
print(" Should end in / if specified.")
|
||||
print(" root_path - Helpful for producing more verbose output if using relative paths.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
uts = UnityTestSummary()
|
||||
try:
|
||||
# look in the specified or current directory for result files
|
||||
if len(sys.argv) > 1:
|
||||
targets_dir = sys.argv[1]
|
||||
else:
|
||||
targets_dir = './'
|
||||
targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '*.test*')))
|
||||
if len(targets) == 0:
|
||||
raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir)
|
||||
uts.set_targets(targets)
|
||||
|
||||
# set the root path
|
||||
if len(sys.argv) > 2:
|
||||
root_path = sys.argv[2]
|
||||
else:
|
||||
root_path = os.path.split(__file__)[0]
|
||||
uts.set_root_path(root_path)
|
||||
|
||||
# run the summarizer
|
||||
print(uts.run())
|
||||
except Exception as e:
|
||||
UnityTestSummary.usage(e)
|
||||
Binary file not shown.
BIN
tests/unity/docs/UnityAssertionsReference.pdf
Normal file
BIN
tests/unity/docs/UnityAssertionsReference.pdf
Normal file
Binary file not shown.
BIN
tests/unity/docs/UnityConfigurationGuide.pdf
Normal file
BIN
tests/unity/docs/UnityConfigurationGuide.pdf
Normal file
Binary file not shown.
BIN
tests/unity/docs/UnityGettingStartedGuide.pdf
Normal file
BIN
tests/unity/docs/UnityGettingStartedGuide.pdf
Normal file
Binary file not shown.
BIN
tests/unity/docs/UnityHelperScriptsGuide.pdf
Normal file
BIN
tests/unity/docs/UnityHelperScriptsGuide.pdf
Normal file
Binary file not shown.
21
tests/unity/docs/license.txt
Normal file
21
tests/unity/docs/license.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) <year> 2007-14 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
71
tests/unity/examples/example_1/makefile
Normal file
71
tests/unity/examples/example_1/makefile
Normal file
@@ -0,0 +1,71 @@
|
||||
# ==========================================
|
||||
# Unity Project - A Test Framework for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
#We try to detect the OS we are running on, and adjust commands as needed
|
||||
ifeq ($(OS),Windows_NT)
|
||||
ifeq ($(shell uname -s),) # not in a bash-like shell
|
||||
CLEANUP = del /F /Q
|
||||
MKDIR = mkdir
|
||||
else # in a bash-like shell, like msys
|
||||
CLEANUP = rm -f
|
||||
MKDIR = mkdir -p
|
||||
endif
|
||||
TARGET_EXTENSION=.exe
|
||||
else
|
||||
CLEANUP = rm -f
|
||||
MKDIR = mkdir -p
|
||||
TARGET_EXTENSION=.out
|
||||
endif
|
||||
|
||||
C_COMPILER=gcc
|
||||
ifeq ($(shell uname -s), Darwin)
|
||||
C_COMPILER=clang
|
||||
endif
|
||||
|
||||
UNITY_ROOT=../..
|
||||
|
||||
CFLAGS=-std=c89
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Wextra
|
||||
CFLAGS += -Wpointer-arith
|
||||
CFLAGS += -Wcast-align
|
||||
CFLAGS += -Wwrite-strings
|
||||
CFLAGS += -Wswitch-default
|
||||
CFLAGS += -Wunreachable-code
|
||||
CFLAGS += -Winit-self
|
||||
CFLAGS += -Wmissing-field-initializers
|
||||
CFLAGS += -Wno-unknown-pragmas
|
||||
CFLAGS += -Wstrict-prototypes
|
||||
CFLAGS += -Wundef
|
||||
CFLAGS += -Wold-style-definition
|
||||
|
||||
TARGET_BASE1=test1
|
||||
TARGET_BASE2=test2
|
||||
TARGET1 = $(TARGET_BASE1)$(TARGET_EXTENSION)
|
||||
TARGET2 = $(TARGET_BASE2)$(TARGET_EXTENSION)
|
||||
SRC_FILES1=$(UNITY_ROOT)/src/unity.c src/ProductionCode.c test/TestProductionCode.c test/test_runners/TestProductionCode_Runner.c
|
||||
SRC_FILES2=$(UNITY_ROOT)/src/unity.c src/ProductionCode2.c test/TestProductionCode2.c test/test_runners/TestProductionCode2_Runner.c
|
||||
INC_DIRS=-Isrc -I$(UNITY_ROOT)/src
|
||||
SYMBOLS=
|
||||
|
||||
all: clean default
|
||||
|
||||
default: $(SRC_FILES1) $(SRC_FILES2)
|
||||
$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) -o $(TARGET1)
|
||||
$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES2) -o $(TARGET2)
|
||||
- ./$(TARGET1)
|
||||
./$(TARGET2)
|
||||
|
||||
test/test_runners/TestProductionCode_Runner.c: test/TestProductionCode.c
|
||||
ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/TestProductionCode.c test/test_runners/TestProductionCode_Runner.c
|
||||
test/test_runners/TestProductionCode2_Runner.c: test/TestProductionCode2.c
|
||||
ruby $(UNITY_ROOT)/auto/generate_test_runner.rb test/TestProductionCode2.c test/test_runners/TestProductionCode2_Runner.c
|
||||
|
||||
clean:
|
||||
$(CLEANUP) $(TARGET1) $(TARGET2)
|
||||
|
||||
ci: CFLAGS += -Werror
|
||||
ci: default
|
||||
5
tests/unity/examples/example_1/readme.txt
Normal file
5
tests/unity/examples/example_1/readme.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
Example 1
|
||||
=========
|
||||
|
||||
Close to the simplest possible example of Unity, using only basic features.
|
||||
Run make to build & run the example tests.
|
||||
24
tests/unity/examples/example_1/src/ProductionCode.c
Normal file
24
tests/unity/examples/example_1/src/ProductionCode.c
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
#include "ProductionCode.h"
|
||||
|
||||
int Counter = 0;
|
||||
int NumbersToFind[9] = { 0, 34, 55, 66, 32, 11, 1, 77, 888 }; /* some obnoxious array to search that is 1-based indexing instead of 0. */
|
||||
|
||||
/* This function is supposed to search through NumbersToFind and find a particular number.
|
||||
* If it finds it, the index is returned. Otherwise 0 is returned which sorta makes sense since
|
||||
* NumbersToFind is indexed from 1. Unfortunately it's broken
|
||||
* (and should therefore be caught by our tests) */
|
||||
int FindFunction_WhichIsBroken(int NumberToFind)
|
||||
{
|
||||
int i = 0;
|
||||
while (i <= 8) /* Notice I should have been in braces */
|
||||
i++;
|
||||
if (NumbersToFind[i] == NumberToFind) /* Yikes! I'm getting run after the loop finishes instead of during it! */
|
||||
return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FunctionWhichReturnsLocalVariable(void)
|
||||
{
|
||||
return Counter;
|
||||
}
|
||||
3
tests/unity/examples/example_1/src/ProductionCode.h
Normal file
3
tests/unity/examples/example_1/src/ProductionCode.h
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
int FindFunction_WhichIsBroken(int NumberToFind);
|
||||
int FunctionWhichReturnsLocalVariable(void);
|
||||
11
tests/unity/examples/example_1/src/ProductionCode2.c
Normal file
11
tests/unity/examples/example_1/src/ProductionCode2.c
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
#include "ProductionCode2.h"
|
||||
|
||||
char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction)
|
||||
{
|
||||
(void)Poor;
|
||||
(void)LittleFunction;
|
||||
/* Since There Are No Tests Yet, This Function Could Be Empty For All We Know.
|
||||
* Which isn't terribly useful... but at least we put in a TEST_IGNORE so we won't forget */
|
||||
return (char*)0;
|
||||
}
|
||||
2
tests/unity/examples/example_1/src/ProductionCode2.h
Normal file
2
tests/unity/examples/example_1/src/ProductionCode2.h
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction);
|
||||
62
tests/unity/examples/example_1/test/TestProductionCode.c
Normal file
62
tests/unity/examples/example_1/test/TestProductionCode.c
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
#include "ProductionCode.h"
|
||||
#include "unity.h"
|
||||
|
||||
/* sometimes you may want to get at local data in a module.
|
||||
* for example: If you plan to pass by reference, this could be useful
|
||||
* however, it should often be avoided */
|
||||
extern int Counter;
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
/* This is run before EACH TEST */
|
||||
Counter = 0x5a5a;
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_FindFunction_WhichIsBroken_ShouldReturnZeroIfItemIsNotInList_WhichWorksEvenInOurBrokenCode(void)
|
||||
{
|
||||
/* All of these should pass */
|
||||
TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(78));
|
||||
TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(1));
|
||||
TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(33));
|
||||
TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(999));
|
||||
TEST_ASSERT_EQUAL(0, FindFunction_WhichIsBroken(-1));
|
||||
}
|
||||
|
||||
void test_FindFunction_WhichIsBroken_ShouldReturnTheIndexForItemsInList_WhichWillFailBecauseOurFunctionUnderTestIsBroken(void)
|
||||
{
|
||||
/* You should see this line fail in your test summary */
|
||||
TEST_ASSERT_EQUAL(1, FindFunction_WhichIsBroken(34));
|
||||
|
||||
/* Notice the rest of these didn't get a chance to run because the line above failed.
|
||||
* Unit tests abort each test function on the first sign of trouble.
|
||||
* Then NEXT test function runs as normal. */
|
||||
TEST_ASSERT_EQUAL(8, FindFunction_WhichIsBroken(8888));
|
||||
}
|
||||
|
||||
void test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValue(void)
|
||||
{
|
||||
/* This should be true because setUp set this up for us before this test */
|
||||
TEST_ASSERT_EQUAL_HEX(0x5a5a, FunctionWhichReturnsLocalVariable());
|
||||
|
||||
/* This should be true because we can still change our answer */
|
||||
Counter = 0x1234;
|
||||
TEST_ASSERT_EQUAL_HEX(0x1234, FunctionWhichReturnsLocalVariable());
|
||||
}
|
||||
|
||||
void test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValueAgain(void)
|
||||
{
|
||||
/* This should be true again because setup was rerun before this test (and after we changed it to 0x1234) */
|
||||
TEST_ASSERT_EQUAL_HEX(0x5a5a, FunctionWhichReturnsLocalVariable());
|
||||
}
|
||||
|
||||
void test_FunctionWhichReturnsLocalVariable_ShouldReturnCurrentCounter_ButFailsBecauseThisTestIsActuallyFlawed(void)
|
||||
{
|
||||
/* Sometimes you get the test wrong. When that happens, you get a failure too... and a quick look should tell
|
||||
* you what actually happened...which in this case was a failure to setup the initial condition. */
|
||||
TEST_ASSERT_EQUAL_HEX(0x1234, FunctionWhichReturnsLocalVariable());
|
||||
}
|
||||
31
tests/unity/examples/example_1/test/TestProductionCode2.c
Normal file
31
tests/unity/examples/example_1/test/TestProductionCode2.c
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
#include "ProductionCode2.h"
|
||||
#include "unity.h"
|
||||
|
||||
/* These should be ignored because they are commented out in various ways:
|
||||
#include "whatever.h"
|
||||
#include "somethingelse.h"
|
||||
*/
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void test_IgnoredTest(void)
|
||||
{
|
||||
TEST_IGNORE_MESSAGE("This Test Was Ignored On Purpose");
|
||||
}
|
||||
|
||||
void test_AnotherIgnoredTest(void)
|
||||
{
|
||||
TEST_IGNORE_MESSAGE("These Can Be Useful For Leaving Yourself Notes On What You Need To Do Yet");
|
||||
}
|
||||
|
||||
void test_ThisFunctionHasNotBeenTested_NeedsToBeImplemented(void)
|
||||
{
|
||||
TEST_IGNORE(); /* Like This */
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* AUTOGENERATED FILE. DO NOT EDIT. */
|
||||
|
||||
/*=======Test Runner Used To Run Each Test Below=====*/
|
||||
#define RUN_TEST(TestFunc, TestLineNum) \
|
||||
{ \
|
||||
Unity.CurrentTestName = #TestFunc; \
|
||||
Unity.CurrentTestLineNumber = TestLineNum; \
|
||||
Unity.NumberOfTests++; \
|
||||
if (TEST_PROTECT()) \
|
||||
{ \
|
||||
setUp(); \
|
||||
TestFunc(); \
|
||||
} \
|
||||
if (TEST_PROTECT()) \
|
||||
{ \
|
||||
tearDown(); \
|
||||
} \
|
||||
UnityConcludeTest(); \
|
||||
}
|
||||
|
||||
/*=======Automagically Detected Files To Include=====*/
|
||||
#include "unity.h"
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
#include "ProductionCode2.h"
|
||||
|
||||
/*=======External Functions This Runner Calls=====*/
|
||||
extern void setUp(void);
|
||||
extern void tearDown(void);
|
||||
extern void test_IgnoredTest(void);
|
||||
extern void test_AnotherIgnoredTest(void);
|
||||
extern void test_ThisFunctionHasNotBeenTested_NeedsToBeImplemented(void);
|
||||
|
||||
|
||||
/*=======Test Reset Option=====*/
|
||||
void resetTest(void);
|
||||
void resetTest(void)
|
||||
{
|
||||
tearDown();
|
||||
setUp();
|
||||
}
|
||||
|
||||
|
||||
/*=======MAIN=====*/
|
||||
int main(void)
|
||||
{
|
||||
UnityBegin("test/TestProductionCode2.c");
|
||||
RUN_TEST(test_IgnoredTest, 18);
|
||||
RUN_TEST(test_AnotherIgnoredTest, 23);
|
||||
RUN_TEST(test_ThisFunctionHasNotBeenTested_NeedsToBeImplemented, 28);
|
||||
|
||||
return (UnityEnd());
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/* AUTOGENERATED FILE. DO NOT EDIT. */
|
||||
|
||||
/*=======Test Runner Used To Run Each Test Below=====*/
|
||||
#define RUN_TEST(TestFunc, TestLineNum) \
|
||||
{ \
|
||||
Unity.CurrentTestName = #TestFunc; \
|
||||
Unity.CurrentTestLineNumber = TestLineNum; \
|
||||
Unity.NumberOfTests++; \
|
||||
if (TEST_PROTECT()) \
|
||||
{ \
|
||||
setUp(); \
|
||||
TestFunc(); \
|
||||
} \
|
||||
if (TEST_PROTECT()) \
|
||||
{ \
|
||||
tearDown(); \
|
||||
} \
|
||||
UnityConcludeTest(); \
|
||||
}
|
||||
|
||||
/*=======Automagically Detected Files To Include=====*/
|
||||
#include "unity.h"
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
#include "ProductionCode.h"
|
||||
|
||||
/*=======External Functions This Runner Calls=====*/
|
||||
extern void setUp(void);
|
||||
extern void tearDown(void);
|
||||
extern void test_FindFunction_WhichIsBroken_ShouldReturnZeroIfItemIsNotInList_WhichWorksEvenInOurBrokenCode(void);
|
||||
extern void test_FindFunction_WhichIsBroken_ShouldReturnTheIndexForItemsInList_WhichWillFailBecauseOurFunctionUnderTestIsBroken(void);
|
||||
extern void test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValue(void);
|
||||
extern void test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValueAgain(void);
|
||||
extern void test_FunctionWhichReturnsLocalVariable_ShouldReturnCurrentCounter_ButFailsBecauseThisTestIsActuallyFlawed(void);
|
||||
|
||||
|
||||
/*=======Test Reset Option=====*/
|
||||
void resetTest(void);
|
||||
void resetTest(void)
|
||||
{
|
||||
tearDown();
|
||||
setUp();
|
||||
}
|
||||
|
||||
|
||||
/*=======MAIN=====*/
|
||||
int main(void)
|
||||
{
|
||||
UnityBegin("test/TestProductionCode.c");
|
||||
RUN_TEST(test_FindFunction_WhichIsBroken_ShouldReturnZeroIfItemIsNotInList_WhichWorksEvenInOurBrokenCode, 20);
|
||||
RUN_TEST(test_FindFunction_WhichIsBroken_ShouldReturnTheIndexForItemsInList_WhichWillFailBecauseOurFunctionUnderTestIsBroken, 30);
|
||||
RUN_TEST(test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValue, 41);
|
||||
RUN_TEST(test_FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValueAgain, 51);
|
||||
RUN_TEST(test_FunctionWhichReturnsLocalVariable_ShouldReturnCurrentCounter_ButFailsBecauseThisTestIsActuallyFlawed, 57);
|
||||
|
||||
return (UnityEnd());
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user