Compare commits

...

132 Commits

Author SHA1 Message Date
Max Bruckner
4fce9cce86 Release version 1.3.0 2017-02-17 01:42:02 +01:00
Max Bruckner
415962da34 Merge pull request #109 from DaveGamble/simplify-parse
Simplify parsing
2017-02-16 21:23:36 +01:00
Max Bruckner
b41264d164 Remove uint8_t because it's not part of C89 2017-02-16 21:22:43 +01:00
Max Bruckner
0f271dcf63 parse_hex4: make input pointer const 2017-02-16 21:02:26 +01:00
Max Bruckner
ead389aba2 parse_value: improve variable names + const correctness 2017-02-16 21:02:25 +01:00
Max Bruckner
698dc528f4 parse_object: improve variable names + const correctness 2017-02-16 21:02:25 +01:00
Max Bruckner
3dc6339025 parse_array: improve variable names + const correctness 2017-02-16 20:08:59 +01:00
Max Bruckner
15592c50f6 parse_number: improve variable names + const correctness 2017-02-16 20:02:18 +01:00
Max Bruckner
efb5e1bc93 parse_string: Improve const correctnes of pointers 2017-02-16 20:00:12 +01:00
Max Bruckner
ace5047782 parse_string: reduce mental burden when reading the code
This restructures parse_string in a way, that you need to keep less
state in your head to understand the code.

This is achieved by:
* only changing the input pointer (current position) at a few places
(not all throughout)
* splitting out the UTF16 handling into a separate function
* renaming the variables so you know what they do without additional
context
2017-02-16 19:49:03 +01:00
Max Bruckner
03f23738bb parse_object: simplify to one do-while loop 2017-02-16 19:49:02 +01:00
Max Bruckner
24dbf29360 parse_array: simplify to one do-while loop 2017-02-16 19:49:02 +01:00
Max Bruckner
b6974ecbc9 Makefile: Update compiler options 2017-02-16 12:52:00 +01:00
Max Bruckner
12b2daccf3 parse_{object,array}: set child only after parsing
This only attaches the parsed linked lists to the items passed to
parse_object and parse_array.
2017-02-16 01:03:43 +01:00
Max Bruckner
f8d0c47bdb Remove unnecessary assignment and silence clang analyzer 2017-02-16 00:31:57 +01:00
Max Bruckner
9f6fa94c91 ensure: replace pow2gt with multiplication by two
This replaces the bit fiddling involved with calculating a new buffer
size by just multiplying the required length by two, paving the way to a
complete switch to size_t at a later point in time.
2017-02-16 00:23:38 +01:00
Max Bruckner
123bb1af7b cJSON: prevent incompatible C and header versions
Introduces a preprocessor directive that aborts the compilation if the
version numbers in the header don't match with the numbers in the c
file.
2017-02-15 23:21:50 +01:00
Max Bruckner
cf862d0fed implement AddItemToObject using AddItemToObjectCS 2017-02-15 21:46:24 +01:00
Max Bruckner
57d105d498 Merge pull request #108 from DaveGamble/cJSON_Invalid
Add new type cJSON_Invalid
2017-02-15 21:28:14 +01:00
Max Bruckner
5986edba1d tests: Ensure that failed parsing returns invalid items 2017-02-15 21:25:52 +01:00
Max Bruckner
c6e1a281f9 tests: assertion macros 2017-02-15 21:25:52 +01:00
Max Bruckner
4f58695ed3 tests: extract common functionality to common.c 2017-02-15 21:25:51 +01:00
Max Bruckner
3facca4792 parse functions: Only set type after successful
This sets the type of an item only if parsing was successful.

This means that in case of failure, the item's type will remain to be
cJSON_Invalid.
2017-02-15 21:25:48 +01:00
Max Bruckner
cf48ea8175 New Type: cJSON_Invalid
This assigns the macro cJSON_Invalid to 0.
2017-02-15 21:19:38 +01:00
Max Bruckner
702fd95af3 fix #106: potentially invalid free in cJSON_AddItemToObject 2017-02-15 20:45:23 +01:00
Max Bruckner
94117a5d23 Fix #105, double free when parse_string fails
This fixes a double free that happens when calling cJSON_Delete on an
item that has been used by parse_string and it failed parsing the
string.

The double free happens, because parse_string frees an alias of
item->valuestring, but doesn't set item->valuestring to NULL.
2017-02-15 15:37:38 +01:00
Max Bruckner
c3bd4463be cJSON_Utils: Guard use of %lu format string for size_t 2017-02-15 13:09:32 +01:00
Max Bruckner
9d7e8f1175 cJSON_Utils: Add casts to unsigned long, fix #103
Thanks @rrvirtual for the suggestion.
2017-02-09 15:09:06 +01:00
Max Bruckner
4047de4f6e fix potential NULL dereferences found by coverity 2017-02-08 03:00:44 +01:00
Max Bruckner
49b9336558 Merge pull request #92 from DaveGamble/tests
Introduce the cunity testing framework
2017-02-07 21:50:07 +01:00
Max Bruckner
d04a2aeccf README: Document additional CMake options 2017-02-07 21:44:37 +01:00
Max Bruckner
33e01ae087 Add support for Travis-CI 2017-02-07 21:35:38 +01:00
Max Bruckner
a09defec4c CMake: Add ENABLE_SANITIZERS flag
Enabling this flag enables AddressSanitizer and
UndefinedBehaviorSanitizer
2017-02-07 21:35:21 +01:00
Max Bruckner
21c02cd3e5 CMake: Add Valgrind support 2017-02-07 21:35:21 +01:00
Max Bruckner
5a36b71a80 unity-tests: parse_value 2017-02-07 21:35:21 +01:00
Max Bruckner
9041570eba unity-tests: parse_object 2017-02-07 21:35:21 +01:00
Max Bruckner
71b05fd4c2 unity-tests: parse_array 2017-02-07 21:35:21 +01:00
Max Bruckner
598b609c45 unity-tests: parse_string 2017-02-07 21:35:20 +01:00
Max Bruckner
b0e5209bde unity-tests: test parse_hex4 2017-02-07 21:35:20 +01:00
Max Bruckner
7fd536d7e0 unity-tests: parse_number 2017-02-07 21:35:20 +01:00
Max Bruckner
be0951dfa4 Move parse tests from test.c -> parse_example.c 2017-02-07 21:35:20 +01:00
Max Bruckner
86be961bb5 test.c: remove file related code 2017-02-07 21:35:19 +01:00
Max Bruckner
bb60d6def5 cunity: parse_examples: test example files
Test parsing and printing example files
2017-02-07 21:35:19 +01:00
Max Bruckner
896e52255b CMake: Build unity as library 2017-02-07 21:35:19 +01:00
Max Bruckner
12acc57967 unity: make it work with -Wconversion 2017-02-07 21:35:19 +01:00
Max Bruckner
d19f3ae890 Merge commit '6b9b57be226a505a9c9cdd9ed029f22495ce04ec' as 'tests/unity' 2017-02-07 21:30:57 +01:00
Max Bruckner
6b9b57be22 Squashed 'tests/unity/' content from commit 1782bab
git-subtree-dir: tests/unity
git-subtree-split: 1782bab0bacd349a45bc215ff91f082912cd7a64
2017-02-07 21:30:57 +01:00
Max Bruckner
e65ea3a45b Merge pull request #101 from DaveGamble/simplify-correctness
Simplify some code and improve correctness
2017-02-07 21:29:11 +01:00
Max Bruckner
87f77274de cJSON_SetNumberValue: Fix undefined double to int conversion
This might cause slight changes in behavior, but it shouldn't break
anything that is not already broken (for example the original macro
could either return a double, or an integer or whatever depending on if
object is NULL or not.)
2017-02-07 21:23:36 +01:00
Max Bruckner
dded751757 parse_string: remove useless first byte marks for utf8 2017-02-07 21:23:36 +01:00
Max Bruckner
d7b5545748 parse_hex4: deduplicate into a for loop 2017-02-07 21:23:36 +01:00
Max Bruckner
0747669972 parse_number: Switch to C library's strtod
Replaces the hand written floating point parser with the C library
implementation.
2017-02-07 21:23:36 +01:00
Max Bruckner
f09bdef15e Revert "test.c: Fix buffer overrun found by coverity"
This reverts commit c866abd842.

The length of this buffer was intentional. This looks like a false
positive.
2017-02-07 21:22:40 +01:00
Max Bruckner
7119a16b5f Merge pull request #100 from DaveGamble/goto-fail
Use goto fail for error handling and fix some memory leaks on the way
2017-02-07 21:13:51 +01:00
Max Bruckner
cc514583cc cJSON_Duplicate: goto fail error handling
Simplify error handling using goto fail and improve some names.
2017-02-07 20:59:55 +01:00
Max Bruckner
021b174ee1 parse_object: goto fail error handling
Makes the control flow easier to reason about and fixes a few potential
memory leaks.
2017-02-07 20:59:55 +01:00
Max Bruckner
99cd9af7d5 parse_array: goto fail error handling
Makes the control flow easier to reason about and fixes a few potential
memory leaks.
2017-02-07 20:59:55 +01:00
Max Bruckner
8656386c4f parse_string: goto fail error handling
Makes the control flow easier to reason about and fixes a few potential
memory leaks.
2017-02-07 20:59:55 +01:00
Max Bruckner
c866abd842 test.c: Fix buffer overrun found by coverity 2017-02-07 20:59:55 +01:00
Max Bruckner
1f5538f79d CMake: Fix several problems with automatic flag detection 2017-02-07 20:56:54 +01:00
Max Bruckner
9ed906758e fix null pointer dereferences found by coverity 2017-02-07 19:07:18 +01:00
Max Bruckner
2f712c7456 CMake: Fix compile flag detection on old CMake 2017-02-07 16:43:33 +01:00
Max Bruckner
d00ca18ac2 CMake: automatic detection of compiler flag compatibility 2017-02-07 14:51:29 +01:00
Max Bruckner
6cdb6894d4 Compiler flags: Add -fstack-protector-strong 2017-02-06 01:51:53 +01:00
Max Bruckner
ee0c920dff fix: add break in UTF-16 handling
Introducing the switch defaults in an earlier commit made UTF-16 \u
sequence handling broken.
2017-02-04 01:54:56 +01:00
Max Bruckner
af6d5d3d7c Add more consts to unsigned chars 2017-02-03 22:41:14 +01:00
Max Bruckner
cf9d57d56c Fix incorrect return 2017-02-03 20:34:37 +01:00
Max Bruckner
4d95639001 Merge pull request #98 from DaveGamble/compiler-options
More compiler flags + correctness improvements
2017-02-03 19:43:11 +01:00
Max Bruckner
3d3bfc6a4d Compiler flag -Wconversion
Makes type conversions explicit, if they alter a value
2017-02-03 18:36:13 +01:00
Max Bruckner
41e2837df1 Fix potentially undefined behavior when filling valueint
If the number is bigger or smaller than the biggest or smallest integer,
the behavior would be undefined.

This commit defines it as saturation behavior.
2017-02-03 18:36:13 +01:00
Max Bruckner
ecd5678527 Change all internal sizes to size_t 2017-02-03 18:36:12 +01:00
Max Bruckner
28b9ba4334 Change all internal strings to unsigned char* 2017-02-03 15:59:00 +01:00
Max Bruckner
b182ced1d6 Compiler flag -Wswitch-default + add defaults 2017-02-03 12:21:36 +01:00
Max Bruckner
fe18403935 Compiler flag -Wundef + fix incorrect macro 2017-02-03 12:14:50 +01:00
Max Bruckner
981f59b163 Release Version 1.2.1 2017-01-30 19:36:36 +01:00
Max Bruckner
e4eadb9a81 Merge pull request #97 from DaveGamble/fix96-null-pointer-dereference
Fix potential null pointer dereference in cJSON_Utils
Fixes #96
2017-01-30 19:34:33 +01:00
Max Bruckner
ff0681e4fd Utils: PatchDetach: Check for invalid patch string 2017-01-30 19:30:16 +01:00
Max Bruckner
a2309a509d Utils: InplaceDecodePointerString: Check for NULL 2017-01-30 19:29:52 +01:00
Max Bruckner
c49ffbfba8 cJSON_Version: returns a version string
This is useful to programmatically find out the version of cJSON that
has been used (useful in case of scripting language bindings for
example).
2017-01-12 20:37:29 +01:00
Max Bruckner
e7533aa6f0 Put version information in the header
This is important so that it is always known which version of the
library is used, especially if the C and Header files have just been
copy pasted to another code base.
2017-01-12 20:35:13 +01:00
Max Bruckner
de8eaaba89 Release version 1.2.0 2017-01-09 12:25:31 +01:00
Max Bruckner
b2da44d6cb Merge pull request #90 from DaveGamble/cJSON_Raw
Add support for raw JSON
2017-01-09 12:22:00 +01:00
Max Bruckner
f6998a6a34 Contributors: Add Jiri Zouhar 2017-01-09 12:02:21 +01:00
Max Bruckner
e3e0b5150b cJSON_CreateRaw: Format fixes 2017-01-05 21:33:52 +01:00
Max Bruckner
1df987a170 cJSON_strdup: Check for NULL string 2017-01-05 21:31:17 +01:00
Max Bruckner
ddadb44a67 cJSON_Raw: Additional checks in print_value 2017-01-05 21:30:37 +01:00
Max Bruckner
9ef44fc0b6 Remove C++ comment in header 2017-01-05 21:12:10 +01:00
Max Bruckner
8c58e62597 Merge remote-tracking branch 'loigu/master' into cJSON_Raw 2017-01-05 21:07:08 +01:00
Max Bruckner
8893e39712 gitignore: ignore *.orig 2017-01-04 20:40:34 +01:00
Max Bruckner
3d6ae11340 Make cJSON C++ compatible 2016-12-28 12:33:20 +01:00
Max Bruckner
a1f2600883 Contributors: Add Romain Porte 2016-12-28 12:29:03 +01:00
Max Bruckner
fcc85bdfbc Gitignore: Add Makefile output 2016-12-28 12:23:56 +01:00
Max Bruckner
7d08a3518a Merge pull request #88 from MicroJoe/fix-readme-tests
Fix bad CMake argument in README
2016-12-28 12:20:37 +01:00
Romain Porte
cc486a0354 Fix bad CMake argument in README 2016-12-27 11:32:41 +01:00
Max Bruckner
fcc89c4bb2 Move increment out of loop condition fixes #85 2016-12-15 11:12:42 +01:00
Max Bruckner
a0431e226f Merge pull request #83 from gatzka/feature/cast_qual_v3
Introduce compiler check if const is casted away.
2016-12-08 21:59:32 +07:00
Stephan Gatzka
89edfb6741 Warn if cast removes a type qualifier like const. 2016-12-08 13:21:30 +01:00
Stephan
e69db83de5 Temporarily disable warning when const is cast away.
There was a long running discussion here
https://github.com/DaveGamble/cJSON/pull/80 how to provide const
correctness for users of cJSON.

To avoid breaking changes for users of cJSON v1 it was decided to
disable this warning.

pragma was tested with gcc 5.4.0/6.2.1 and clang 3.8/3.9.
2016-12-08 13:21:18 +01:00
Max Bruckner
5310da351d contributors: Add Alper Akcan 2016-12-06 09:01:12 +07:00
Max Bruckner
68196f5b14 Release version 1.1.0 2016-12-06 08:56:23 +07:00
Max Bruckner
d992cd46e6 test: Handle cJSON_PrintPreallocated return values correctly 2016-12-06 08:47:44 +07:00
Max Bruckner
84b48121bb header: explain PrintPreallocated return value 2016-12-06 08:46:48 +07:00
Max Bruckner
f10e2f884a test: exit on malloc failure 2016-12-06 08:41:51 +07:00
Max Bruckner
eeb6d237b6 Merge pull request #81 from alperakcan/master
fix memory leak in cJSON_Duplicate
2016-12-06 08:36:47 +07:00
Alper Akcan
5b4420298f fix memory leak in cJSON_Duplicate 2016-12-05 14:23:04 +03:00
Max Bruckner
e1eb06fae0 cJSON.h warn about write access to constants (#80)
Add a comment that warns about potential write access to constant memory
locations after using cJSON_AddItemToObjectCS.
2016-11-30 22:14:16 +07:00
Max Bruckner
6aefa45056 Merge pull request #79 from gatzka/fix/test
Make print_preallocated static.
2016-11-29 08:19:33 +07:00
Stephan Gatzka
345d57e9e5 Make print_preallocated static.
Otherwise compilation failes due to missing prototypes.
2016-11-28 20:21:38 +01:00
Max Bruckner
3da18a2df9 contributors: Add Kyle Chisholm 2016-11-29 00:01:21 +07:00
Max Bruckner
bf17703012 Merge branch 'print_preallocated' 2016-11-28 23:08:25 +07:00
Max Bruckner
6622c54f18 Handle out of memory when printing string 2016-11-28 23:06:38 +07:00
Kyle Chisholm
8df4cd46eb more concise return 2016-11-28 22:59:55 +07:00
Kyle Chisholm
3917fb5255 use print_preallocated function to test printout of JSON 2016-11-28 22:59:55 +07:00
Kyle Chisholm
99896d879c function to print and compare to preallocated buffer 2016-11-28 22:59:55 +07:00
Kyle Chisholm
4d1dcaa160 check print_value return 2016-11-28 22:59:54 +07:00
Kyle Chisholm
de93d76d0b changed to cJSON_PrintPreallocated, added flag in printbuffer 2016-11-28 22:59:53 +07:00
Kyle Chisholm
602c241a0b fixed "make test" on Mac (clang) 2016-11-28 22:53:36 +07:00
Kyle Chisholm
23dafa4739 added Print function call for pre-allocated buffer 2016-11-28 22:53:33 +07:00
Max Bruckner
59cf4112d2 Merge pull request #78 from gatzka/feature/gcc_check_strict_overflow_v2
gcc check strict overflow v2
2016-11-28 22:29:03 +07:00
Stephan
5cfda2292f Warn if compiler optimizes based on asumption signed overflow does not occur.
This switch is only active when making a release build.
2016-11-28 09:12:17 +01:00
Stephan
038b04d80a Take out len from condition check.
Otherwise, the check is just undefined behaviour. gcc even takes out
this check because len can never be zero if len does not wrap around.

Found with -Wstrict-overflow=2
2016-11-28 09:11:14 +01:00
Max Bruckner
624bc85143 contributors: add Stephan Gatzka 2016-11-28 01:03:04 +07:00
Max Bruckner
1f0ad823c9 Merge pull request #75 from gatzka/feature/enhance_gcc_checks_v2
Enhance gcc checks v2
2016-11-28 00:58:45 +07:00
Stephan Gatzka
29b6643bab Warn about missing prototypes.
This switch warns about missing prototypes. It does not warn if
functions are declared static. So this switch encourages the usage of
static whenever possible. This enables the compiler to perform inlining.
2016-11-27 18:22:12 +01:00
Stephan Gatzka
a8e1368697 Make function static when possible.
This is a prerequisite to later enable -Wmissing-prototypes.
2016-11-27 18:20:58 +01:00
Stephan Gatzka
bea0be48b6 Perform additional format checks. 2016-11-27 18:20:41 +01:00
Stephan Gatzka
91d11cd050 Warn if casting a pointer increases alignment of target. 2016-11-27 18:19:52 +01:00
Stephan Gatzka
9591ecddbe Warn about variables initialized by themselves. 2016-11-27 18:19:07 +01:00
Stephan Gatzka
1568015de6 Warn about shadowing variables and functions. 2016-11-27 18:18:00 +01:00
Stephan Gatzka
7ed067e680 Rename some variables to avoid shadowing. 2016-11-27 18:16:28 +01:00
Max Bruckner
4703f01cf4 Make/CMake: Fix use of compiler flags
CMake: Option was not enabled by default as intended.
Makefile: Flags were not passed when building the tests
2016-11-26 22:09:20 +07:00
Jiri Zouhar
06008b0444 add support to insert raw json 2016-11-14 11:20:10 +01:00
167 changed files with 19159 additions and 906 deletions

5
.gitignore vendored
View File

@@ -8,3 +8,8 @@ test
tags
*.dylib
build/
cJSON_test
cJSON_test_utils
libcjson.so.*
libcjson_utils.so.*
*.orig

28
.travis.yml Normal file
View 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

View File

@@ -1,24 +1,74 @@
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
cmake_minimum_required(VERSION 2.8)
subdirs(tests)
include(GNUInstallDirs)
project(cJSON C)
set(PROJECT_VERSION_MAJOR 1)
set(PROJECT_VERSION_MINOR 0)
set(PROJECT_VERSION_PATCH 2)
set(PROJECT_VERSION_MINOR 3)
set(PROJECT_VERSION_PATCH 0)
set(CJSON_VERSION_SO 1)
set(CJSON_UTILS_VERSION_SO 1)
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
option(ENABLE_CUSTOM_COMPILER_FLAGS ON)
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")
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
)
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()
# 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 +148,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}")

View File

@@ -2,6 +2,7 @@ Contributors
============
* [Ajay Bhargav](https://github.com/ajaybhargav)
* [Alper Akcan](https://github.com/alperakcan)
* [Anton Sergeev](https://github.com/anton-sergeev)
* [Christian Schulze](https://github.com/ChristianSch)
* [Dave Gamble](https://github.com/DaveGamble)
@@ -13,8 +14,10 @@ Contributors
* Ian Mobley
* Irwan Djadjadi
* [IvanVoid](https://github.com/npi3pak)
* [Jiri Zouhar](https://github.com/loigu)
* [Jonathan Fether](https://github.com/jfether)
* [Kevin Branigan](https://github.com/kbranigan)
* [Kyle Chisholm](https://github.com/ChisholmKyle)
* [Linus Wallgren](https://github.com/ecksun)
* [Max Bruckner](https://github.com/FSMaxB)
* Mike Pontillo
@@ -22,6 +25,8 @@ Contributors
* [Rafael Leal Dias](https://github.com/rafaeldias)
* [Rod Vagg](https://github.com/rvagg)
* [Roland Meertens](https://github.com/rmeertens)
* [Romain Porte](https://github.com/MicroJoe)
* [Stephan Gatzka](https://github.com/gatzka)
* [Weston Schmidt](https://github.com/schmidtw)
And probably more people on [SourceForge](https://sourceforge.net/p/cjson/bugs/search/?q=status%3Aclosed-rejected+or+status%3Aclosed-out-of-date+or+status%3Awont-fix+or+status%3Aclosed-fixed+or+status%3Aclosed&page=0)

View File

@@ -5,9 +5,12 @@ UTILS_LIBNAME = libcjson_utils
CJSON_TEST = cJSON_test
UTILS_TEST = cJSON_test_utils
CJSON_TEST_SRC = cJSON.c test.c
UTILS_TEST_SRC = cJSON.c cJSON_Utils.c test_utils.c
LDLIBS = -lm
LIBVERSION = 1.0.2
LIBVERSION = 1.3.0
CJSON_SOVERSION = 1
UTILS_SOVERSION = 1
@@ -20,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 $(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')
@@ -62,15 +65,15 @@ test: tests
./$(UTILS_TEST)
.c.o:
$(CC) -ansi -pedantic -c $(R_CFLAGS) $<
$(CC) -c $(R_CFLAGS) $<
#tests
#cJSON
$(CJSON_TEST): cJSON.c cJSON.h test.c
$(CC) $^ -o $@ $(LDLIBS) -I.
$(CJSON_TEST): $(CJSON_TEST_SRC) cJSON.h
$(CC) $(R_CFLAGS) $(CJSON_TEST_SRC) -o $@ $(LDLIBS) -I.
#cJSON_Utils
$(UTILS_TEST): cJSON.c cJSON.h test.c
$(CC) $^ -o $@ $(LDLIBS) -I.
$(UTILS_TEST): $(UTILS_TEST_SRC) cJSON.h cJSON_Utils.h
$(CC) $(R_CFLAGS) $(UTILS_TEST_SRC) -o $@ $(LDLIBS) -I.
#static libraries
#cJSON

View File

@@ -79,10 +79,12 @@ make
And install it with `make install` if you want. By default it installs the headers `/usr/local/include/cjson` and the libraries to `/usr/local/lib`. It also installs files for pkg-config to make it easier to detect and use an existing installation of CMake. And it installs CMake config files, that can be used by other CMake based projects to discover the library.
You can change the build process with a list of different options that you can pass to CMake. Turn them on with `On` and off with `Off`:
* `-DENABLE_CJSON_TESTS=On`: Enable building the tests. (on by default)
* `-DENABLE_CJSON_TEST=On`: Enable building the tests. (on by default)
* `-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.
@@ -90,7 +92,7 @@ If you are packaging cJSON for a distribution of Linux, you would probably take
```
mkdir build
cd build
cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TESTS=Off -DCMAKE_INSTALL_PREFIX=/usr
cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_CJSON_TEST=Off -DCMAKE_INSTALL_PREFIX=/usr
make
make DESTDIR=$pkgdir install
```

1467
cJSON.c

File diff suppressed because it is too large Load Diff

28
cJSON.h
View File

@@ -28,9 +28,18 @@ extern "C"
{
#endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 3
#define CJSON_VERSION_PATCH 0
/* 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)
@@ -38,6 +47,7 @@ extern "C"
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
@@ -54,7 +64,7 @@ typedef struct cJSON
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String */
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* The item's number, if type==cJSON_Number */
int valueint;
@@ -83,6 +93,8 @@ extern char *cJSON_Print(const cJSON *item);
extern 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);
/* 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);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
@@ -103,6 +115,8 @@ extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern 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);
@@ -115,7 +129,10 @@ extern 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);
extern void cJSON_AddItemToObjectCS(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 */
/* 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);
/* 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);
@@ -150,10 +167,13 @@ extern void cJSON_Minify(char *json);
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
#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 */
extern double cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object) ? 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)

View File

@@ -2,15 +2,17 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#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 +21,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)
{
@@ -37,11 +39,11 @@ 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)
{
@@ -76,9 +78,9 @@ 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;
size_t l = 0;
for (; *s; s++, l++)
{
if ((*s == '~') || (*s == '/'))
@@ -90,7 +92,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++)
{
@@ -116,39 +118,47 @@ static void cJSONUtils_PointerEncodedstrcpy(char *d, const char *s)
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++)
{
char *found = cJSONUtils_FindPointerFromObjectTo(obj, target);
unsigned char *found = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(obj, target);
if (found)
{
if ((type & 0xFF) == cJSON_Array)
{
/* 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> */
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)
{
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 ((type & 0xFF) == cJSON_Object)
{
char *ret = (char*)malloc(strlen(found) + cJSONUtils_PointerEncodedstrlen(obj->string) + 2);
unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + cJSONUtils_PointerEncodedstrlen((unsigned char*)obj->string) + 2);
*ret = '/';
cJSONUtils_PointerEncodedstrcpy(ret + 1, obj->string);
strcat(ret, found);
cJSONUtils_PointerEncodedstrcpy(ret + 1, (unsigned char*)obj->string);
strcat((char*)ret, (char*)found);
free(found);
return ret;
return (char*)ret;
}
/* reached leaf of the tree, found nothing */
@@ -168,24 +178,28 @@ cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer)
{
if ((object->type & 0xFF) == cJSON_Array)
{
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;
}
object = cJSON_GetArrayItem(object, (int)which);
}
else if ((object->type & 0xFF) == cJSON_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,9 +219,14 @@ 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++)
{
*s2 = (*string != '~')
@@ -220,22 +239,29 @@ 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;
/* copy path and split it in parent and child */
parentptr = cJSONUtils_strdup(path);
childptr = strrchr(parentptr, '/'); /* last '/' */
if (childptr)
{
/* split strings */
*childptr++ = '\0';
if (parentptr == NULL) {
return NULL;
}
parent = cJSONUtils_GetPointer(object, parentptr);
childptr = (unsigned char*)strrchr((char*)parentptr, '/'); /* last '/' */
if (childptr == NULL)
{
free(parentptr);
return NULL;
}
/* split strings */
*childptr++ = '\0';
parent = cJSONUtils_GetPointer(object, (char*)parentptr);
cJSONUtils_InplaceDecodePointerString(childptr);
if (!parent)
@@ -245,11 +271,11 @@ static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path)
}
else if ((parent->type & 0xFF) == cJSON_Array)
{
ret = cJSON_DetachItemFromArray(parent, atoi(childptr));
ret = cJSON_DetachItemFromArray(parent, atoi((char*)childptr));
}
else if ((parent->type & 0xFF) == cJSON_Object)
{
ret = cJSON_DetachItemFromObject(parent, childptr);
ret = cJSON_DetachItemFromObject(parent, (char*)childptr);
}
free(parentptr);
@@ -259,7 +285,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;
@@ -292,7 +318,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;
@@ -322,8 +348,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");
@@ -369,7 +395,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. */
@@ -390,7 +416,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)
{
@@ -431,13 +457,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. */
@@ -450,19 +476,19 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
}
else if ((parent->type & 0xFF) == cJSON_Array)
{
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)
{
cJSON_DeleteItemFromObject(parent, childptr);
cJSON_AddItemToObject(parent, childptr, value);
cJSON_DeleteItemFromObject(parent, (char*)childptr);
cJSON_AddItemToObject(parent, (char*)childptr, value);
}
else
{
@@ -476,6 +502,12 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
{
int err = 0;
if (patches == NULL)
{
return 1;
}
if ((patches->type & 0xFF) != cJSON_Array)
{
/* malformed patches. */
@@ -497,20 +529,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)
{
@@ -521,14 +553,19 @@ static void cJSONUtils_GeneratePatch(cJSON *patches, const char *op, const char
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;
}
@@ -537,37 +574,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++)
{
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++)
{
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++)
{
cJSONUtils_GeneratePatch(patches, "add", path, "-", to);
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to);
}
free(newpath);
return;
@@ -585,12 +638,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);
@@ -600,13 +653,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;
}
}
@@ -621,7 +674,7 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *f
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;
}
@@ -639,7 +692,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;
@@ -676,7 +729,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)
{

253
test.c
View File

@@ -22,52 +22,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
/* Parse text to JSON, then render back to text, and print! */
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);
}
}
/* Read a file, parse, render back, etc. */
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);
fread(data, 1, len, f);
data[len] = '\0';
fclose(f);
doit(data);
free(data);
}
/* Used by some code below as an example datatype. */
struct record
{
@@ -81,8 +38,75 @@ struct record
const char *country;
};
/* Create a bunch of objects as demonstration. */
void create_objects(void)
static int print_preallocated(cJSON *root)
{
/* declarations */
char *out = NULL;
char *buf = NULL;
char *buf_fail = NULL;
size_t len = 0;
size_t len_fail = 0;
/* formatted print */
out = cJSON_Print(root);
/* create buffer to succeed */
/* the extra 64 bytes are in case a floating point value is printed */
len = strlen(out) + 64;
buf = (char*)malloc(len);
if (buf == NULL)
{
printf("Failed to allocate memory.\n");
exit(1);
}
/* create buffer to fail */
len_fail = strlen(out);
buf_fail = (char*)malloc(len_fail);
if (buf_fail == NULL)
{
printf("Failed to allocate memory.\n");
exit(1);
}
/* Print to buffer */
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");
printf("cJSON_Print result:\n%s\n", out);
printf("cJSON_PrintPreallocated result:\n%s\n", buf);
}
free(out);
free(buf_fail);
free(buf);
return -1;
}
/* success */
printf("%s\n", buf);
/* force it to fail */
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);
free(out);
free(buf_fail);
free(buf);
return -1;
}
free(out);
free(buf_fail);
free(buf);
return 0;
}
/* Create a bunch of objects as demonstration. */
static void create_objects(void)
{
/* declare a few. */
cJSON *root = NULL;
@@ -90,7 +114,6 @@ void create_objects(void)
cJSON *img = NULL;
cJSON *thm = NULL;
cJSON *fld = NULL;
char *out = NULL;
int i = 0;
/* Our "days of the week" array: */
@@ -152,21 +175,20 @@ void create_objects(void)
cJSON_AddNumberToObject(fmt, "frame rate", 24);
/* Print to text */
out = cJSON_Print(root);
/* Delete the cJSON */
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
/* print it */
printf("%s\n",out);
/* release the string */
free(out);
/* Our "days of the week" array: */
root = cJSON_CreateStringArray(strings, 7);
out = cJSON_Print(root);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);
/* Our matrix: */
root = cJSON_CreateArray();
@@ -177,11 +199,11 @@ void create_objects(void)
/* cJSON_ReplaceItemInArray(root, 1, cJSON_CreateString("Replacement")); */
out = cJSON_Print(root);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);
/* Our "gallery" item: */
root = cJSON_CreateObject();
@@ -195,13 +217,13 @@ void create_objects(void)
cJSON_AddStringToObject(thm, "Width", "100");
cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4));
out = cJSON_Print(root);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);
/* Our array of "records": */
root = cJSON_CreateArray();
for (i = 0; i < 2; i++)
{
@@ -218,109 +240,26 @@ void create_objects(void)
/* cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root, 1), "City", cJSON_CreateIntArray(ids, 4)); */
out = cJSON_Print(root);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);
root = cJSON_CreateObject();
cJSON_AddNumberToObject(root, "number", 1.0 / zero);
out = cJSON_Print(root);
if (print_preallocated(root) != 0) {
cJSON_Delete(root);
exit(EXIT_FAILURE);
}
cJSON_Delete(root);
printf("%s\n", out);
free(out);
}
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";
/* 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"); */
/* print the version */
printf("Version: %s\n", cJSON_Version());
/* Now some samplecode for building objects concisely: */
create_objects();

View File

@@ -96,14 +96,14 @@ int main(void)
printf("JSON Apply Patch Tests\n");
for (i = 0; i < 15; i++)
{
cJSON *object = cJSON_Parse(patches[i][0]);
cJSON *object_to_be_patched = cJSON_Parse(patches[i][0]);
cJSON *patch = cJSON_Parse(patches[i][1]);
int err = cJSONUtils_ApplyPatches(object, patch);
char *output = cJSON_Print(object);
int err = cJSONUtils_ApplyPatches(object_to_be_patched, patch);
char *output = cJSON_Print(object_to_be_patched);
printf("Test %d (err %d):\n%s\n\n", i + 1, err, output);
free(output);
cJSON_Delete(object);
cJSON_Delete(object_to_be_patched);
cJSON_Delete(patch);
}
@@ -168,19 +168,19 @@ int main(void)
printf("JSON Merge Patch tests\n");
for (i = 0; i < 15; i++)
{
cJSON *object = cJSON_Parse(merges[i][0]);
cJSON *object_to_be_merged = cJSON_Parse(merges[i][0]);
cJSON *patch = cJSON_Parse(merges[i][1]);
char *before = cJSON_PrintUnformatted(object);
char *before_merge = cJSON_PrintUnformatted(object_to_be_merged);
patchtext = cJSON_PrintUnformatted(patch);
printf("Before: [%s] -> [%s] = ", before, patchtext);
object = cJSONUtils_MergePatch(object, patch);
after = cJSON_PrintUnformatted(object);
printf("Before: [%s] -> [%s] = ", before_merge, patchtext);
object_to_be_merged = cJSONUtils_MergePatch(object_to_be_merged, patch);
after = cJSON_PrintUnformatted(object_to_be_merged);
printf("[%s] vs [%s] (%s)\n", after, merges[i][2], strcmp(after, merges[i][2]) ? "FAIL" : "OK");
free(before);
free(before_merge);
free(patchtext);
free(after);
cJSON_Delete(object);
cJSON_Delete(object_to_be_merged);
cJSON_Delete(patch);
}

43
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,43 @@
add_library(unity unity/src/unity.c)
if(ENABLE_CJSON_TEST)
#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
)
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()
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
View 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"
extern void reset(cJSON *item)
{
if ((item != NULL) && (item->child != NULL))
{
cJSON_Delete(item->child);
}
if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference))
{
cJSON_free(item->valuestring);
}
if ((item->string != NULL) && !(item->type & cJSON_StringIsConst))
{
cJSON_free(item->string);
}
memset(item, 0, sizeof(cJSON));
}
extern 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;
}

52
tests/common.h Normal file
View File

@@ -0,0 +1,52 @@
/*
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"
extern void reset(cJSON *item);
extern char *read_file(const char *filename);
extern cjbool assert_is_invalid(cJSON *item);
/* 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

View 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
View File

@@ -0,0 +1 @@
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

View File

@@ -0,0 +1 @@
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

8
tests/inputs/test11 Normal file
View File

@@ -0,0 +1,8 @@
{
"name": "Jack (\"Bee\") Nimble",
"format": {"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,"frame rate": 24
}
}

View File

@@ -0,0 +1,10 @@
{
"name": "Jack (\"Bee\") Nimble",
"format": {
"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,
"frame rate": 24
}
}

View File

@@ -0,0 +1,18 @@
{
"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [{
"value": "New",
"onclick": "CreateNewDoc()"
}, {
"value": "Open",
"onclick": "OpenDoc()"
}, {
"value": "Close",
"onclick": "CloseDoc()"
}]
}
}
}

View 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;"
}
}
}

View 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"
}
}
}

View 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
View 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"
}
]

View File

@@ -0,0 +1,19 @@
[{
"precision": "zip",
"Latitude": 37.766800,
"Longitude": -122.395900,
"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
tests/inputs/test8 Normal file
View 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]
}
}

View 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
View File

@@ -0,0 +1,5 @@
[
[0, -1, 0],
[1, 0, 0],
[0, 0, 1]
]

View File

@@ -0,0 +1 @@
[[0, -1, 0], [1, 0, 0], [0, 0, 1]]

158
tests/parse_array.c Normal file
View 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));
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));
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);
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
View 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
View 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
View 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
View 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));
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));
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_array_should_parse_arrays_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);
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_array_should_parse_arrays_with_one_element);
return UNITY_END();
}

125
tests/parse_string.c Normal file
View 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), "Couldn't parse string.");
assert_is_string(item);
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, item->valuestring, "The parsed result isn't as expected.");
cJSON_free(item->valuestring);
item->valuestring = NULL;
}
#define assert_not_parse_string(string) \
TEST_ASSERT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer), "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
View 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));
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();
}

30
tests/unity/.gitattributes vendored Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,211 @@
Unity Test API
==============
[![Unity Build Status](https://api.travis-ci.org/ThrowTheSwitch/Unity.png?branch=master)](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() == 0)
{
MyTest();
}
}
If MyTest calls `TEST_ABORT`, program control will immediately return to `TEST_PROTECT` with a non-zero return value.
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.

View 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

View 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

View 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
//-------------------------------------------

View 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

View 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

View File

@@ -0,0 +1,191 @@
#============================================================
# 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 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 @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 @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
# 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
# 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

View 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>&#xD;[File] #{filename}&#xD;[Line] #{item[:line]}&#xD;</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>&#xD;[File] #{filename}&#xD;[Line] #{item[:line]}&#xD;</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

View 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

View 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

View 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)

View 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

View 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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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.

View 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

View 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.

View 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;
}

View File

@@ -0,0 +1,3 @@
int FindFunction_WhichIsBroken(int NumberToFind);
int FunctionWhichReturnsLocalVariable(void);

View 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;
}

View File

@@ -0,0 +1,2 @@
char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction);

View 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());
}

View 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 */
}

View File

@@ -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());
}

View File

@@ -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());
}

View File

@@ -0,0 +1,70 @@
# ==========================================
# 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=c99
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=all_tests
TARGET1 = $(TARGET_BASE1)$(TARGET_EXTENSION)
SRC_FILES1=\
$(UNITY_ROOT)/src/unity.c \
$(UNITY_ROOT)/extras/fixture/src/unity_fixture.c \
src/ProductionCode.c \
src/ProductionCode2.c \
test/TestProductionCode.c \
test/TestProductionCode2.c \
test/test_runners/TestProductionCode_Runner.c \
test/test_runners/TestProductionCode2_Runner.c \
test/test_runners/all_tests.c
INC_DIRS=-Isrc -I$(UNITY_ROOT)/src -I$(UNITY_ROOT)/extras/fixture/src
SYMBOLS=
all: clean default
default:
$(C_COMPILER) $(CFLAGS) $(INC_DIRS) $(SYMBOLS) $(SRC_FILES1) -o $(TARGET1)
- ./$(TARGET1) -v
clean:
$(CLEANUP) $(TARGET1)
ci: CFLAGS += -Werror
ci: default

View File

@@ -0,0 +1,5 @@
Example 2
=========
Same as the first example, but now using Unity's test fixture to group tests
together. Using the test fixture also makes writing test runners much easier.

View 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;
}

View File

@@ -0,0 +1,3 @@
int FindFunction_WhichIsBroken(int NumberToFind);
int FunctionWhichReturnsLocalVariable(void);

View 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;
}

View File

@@ -0,0 +1,2 @@
char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction);

View File

@@ -0,0 +1,64 @@
#include "ProductionCode.h"
#include "unity.h"
#include "unity_fixture.h"
TEST_GROUP(ProductionCode);
//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;
TEST_SETUP(ProductionCode)
{
//This is run before EACH TEST
Counter = 0x5a5a;
}
TEST_TEAR_DOWN(ProductionCode)
{
}
TEST(ProductionCode, FindFunction_WhichIsBroken_ShouldReturnZeroIfItemIsNotInList_WhichWorksEvenInOurBrokenCode)
{
//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));
}
TEST(ProductionCode, FindFunction_WhichIsBroken_ShouldReturnTheIndexForItemsInList_WhichWillFailBecauseOurFunctionUnderTestIsBroken)
{
// 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));
}
TEST(ProductionCode, FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValue)
{
//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());
}
TEST(ProductionCode, FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValueAgain)
{
//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());
}
TEST(ProductionCode, FunctionWhichReturnsLocalVariable_ShouldReturnCurrentCounter_ButFailsBecauseThisTestIsActuallyFlawed)
{
//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());
}

View File

@@ -0,0 +1,33 @@
#include "ProductionCode2.h"
#include "unity.h"
#include "unity_fixture.h"
TEST_GROUP(ProductionCode2);
/* These should be ignored because they are commented out in various ways:
#include "whatever.h"
*/
//#include "somethingelse.h"
TEST_SETUP(ProductionCode2)
{
}
TEST_TEAR_DOWN(ProductionCode2)
{
}
TEST(ProductionCode2, IgnoredTest)
{
TEST_IGNORE_MESSAGE("This Test Was Ignored On Purpose");
}
TEST(ProductionCode2, AnotherIgnoredTest)
{
TEST_IGNORE_MESSAGE("These Can Be Useful For Leaving Yourself Notes On What You Need To Do Yet");
}
TEST(ProductionCode2, ThisFunctionHasNotBeenTested_NeedsToBeImplemented)
{
TEST_IGNORE(); //Like This
}

View File

@@ -0,0 +1,9 @@
#include "unity.h"
#include "unity_fixture.h"
TEST_GROUP_RUNNER(ProductionCode2)
{
RUN_TEST_CASE(ProductionCode2, IgnoredTest);
RUN_TEST_CASE(ProductionCode2, AnotherIgnoredTest);
RUN_TEST_CASE(ProductionCode2, ThisFunctionHasNotBeenTested_NeedsToBeImplemented);
}

View File

@@ -0,0 +1,11 @@
#include "unity.h"
#include "unity_fixture.h"
TEST_GROUP_RUNNER(ProductionCode)
{
RUN_TEST_CASE(ProductionCode, FindFunction_WhichIsBroken_ShouldReturnZeroIfItemIsNotInList_WhichWorksEvenInOurBrokenCode);
RUN_TEST_CASE(ProductionCode, FindFunction_WhichIsBroken_ShouldReturnTheIndexForItemsInList_WhichWillFailBecauseOurFunctionUnderTestIsBroken);
RUN_TEST_CASE(ProductionCode, FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValue);
RUN_TEST_CASE(ProductionCode, FunctionWhichReturnsLocalVariable_ShouldReturnTheCurrentCounterValueAgain);
RUN_TEST_CASE(ProductionCode, FunctionWhichReturnsLocalVariable_ShouldReturnCurrentCounter_ButFailsBecauseThisTestIsActuallyFlawed);
}

View File

@@ -0,0 +1,12 @@
#include "unity_fixture.h"
static void RunAllTests(void)
{
RUN_TEST_GROUP(ProductionCode);
RUN_TEST_GROUP(ProductionCode2);
}
int main(int argc, const char * argv[])
{
return UnityMain(argc, argv, RunAllTests);
}

View File

@@ -0,0 +1,10 @@
#include "unity.h"
#include "UnityHelper.h"
#include <stdio.h>
#include <string.h>
void AssertEqualExampleStruct(const EXAMPLE_STRUCT_T expected, const EXAMPLE_STRUCT_T actual, const unsigned short line)
{
UNITY_TEST_ASSERT_EQUAL_INT(expected.x, actual.x, line, "Example Struct Failed For Field x");
UNITY_TEST_ASSERT_EQUAL_INT(expected.y, actual.y, line, "Example Struct Failed For Field y");
}

View File

@@ -0,0 +1,12 @@
#ifndef _TESTHELPER_H
#define _TESTHELPER_H
#include "Types.h"
void AssertEqualExampleStruct(const EXAMPLE_STRUCT_T expected, const EXAMPLE_STRUCT_T actual, const unsigned short line);
#define UNITY_TEST_ASSERT_EQUAL_EXAMPLE_STRUCT_T(expected, actual, line, message) AssertEqualExampleStruct(expected, actual, line);
#define TEST_ASSERT_EQUAL_EXAMPLE_STRUCT_T(expected, actual) UNITY_TEST_ASSERT_EQUAL_EXAMPLE_STRUCT_T(expected, actual, __LINE__, NULL);
#endif // _TESTHELPER_H

View File

@@ -0,0 +1,43 @@
HERE = File.expand_path(File.dirname(__FILE__)) + '/'
UNITY_ROOT = File.expand_path(File.dirname(__FILE__)) + '/../..'
require 'rake'
require 'rake/clean'
require HERE+'rakefile_helper'
TEMP_DIRS = [
File.join(HERE, 'build')
]
TEMP_DIRS.each do |dir|
directory(dir)
CLOBBER.include(dir)
end
task :prepare_for_tests => TEMP_DIRS
include RakefileHelpers
# Load default configuration, for now
DEFAULT_CONFIG_FILE = 'target_gcc_32.yml'
configure_toolchain(DEFAULT_CONFIG_FILE)
task :unit => [:prepare_for_tests] do
run_tests get_unit_test_files
end
desc "Generate test summary"
task :summary do
report_summary
end
desc "Build and test Unity"
task :all => [:clean, :unit, :summary]
task :default => [:clobber, :all]
task :ci => [:default]
task :cruise => [:default]
desc "Load configuration"
task :config, :config_file do |t, args|
configure_toolchain(args[:config_file])
end

View File

@@ -0,0 +1,258 @@
require 'yaml'
require 'fileutils'
require UNITY_ROOT+'/auto/unity_test_summary'
require UNITY_ROOT+'/auto/generate_test_runner'
require UNITY_ROOT+'/auto/colour_reporter'
module RakefileHelpers
C_EXTENSION = '.c'
def load_configuration(config_file)
$cfg_file = config_file
$cfg = YAML.load(File.read($cfg_file))
end
def configure_clean
CLEAN.include($cfg['compiler']['build_path'] + '*.*') unless $cfg['compiler']['build_path'].nil?
end
def configure_toolchain(config_file=DEFAULT_CONFIG_FILE)
config_file += '.yml' unless config_file =~ /\.yml$/
load_configuration(config_file)
configure_clean
end
def get_unit_test_files
path = $cfg['compiler']['unit_tests_path'] + 'Test*' + C_EXTENSION
path.gsub!(/\\/, '/')
FileList.new(path)
end
def get_local_include_dirs
include_dirs = $cfg['compiler']['includes']['items'].dup
include_dirs.delete_if {|dir| dir.is_a?(Array)}
return include_dirs
end
def extract_headers(filename)
includes = []
lines = File.readlines(filename)
lines.each do |line|
m = line.match(/^\s*#include\s+\"\s*(.+\.[hH])\s*\"/)
if not m.nil?
includes << m[1]
end
end
return includes
end
def find_source_file(header, paths)
paths.each do |dir|
src_file = dir + header.ext(C_EXTENSION)
if (File.exists?(src_file))
return src_file
end
end
return nil
end
def tackit(strings)
if strings.is_a?(Array)
result = "\"#{strings.join}\""
else
result = strings
end
return result
end
def squash(prefix, items)
result = ''
items.each { |item| result += " #{prefix}#{tackit(item)}" }
return result
end
def build_compiler_fields
command = tackit($cfg['compiler']['path'])
if $cfg['compiler']['defines']['items'].nil?
defines = ''
else
defines = squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items'])
end
options = squash('', $cfg['compiler']['options'])
includes = squash($cfg['compiler']['includes']['prefix'], $cfg['compiler']['includes']['items'])
includes = includes.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR)
return {:command => command, :defines => defines, :options => options, :includes => includes}
end
def compile(file, defines=[])
compiler = build_compiler_fields
cmd_str = "#{compiler[:command]}#{compiler[:defines]}#{compiler[:options]}#{compiler[:includes]} #{file} " +
"#{$cfg['compiler']['object_files']['prefix']}#{$cfg['compiler']['object_files']['destination']}"
obj_file = "#{File.basename(file, C_EXTENSION)}#{$cfg['compiler']['object_files']['extension']}"
execute(cmd_str + obj_file)
return obj_file
end
def build_linker_fields
command = tackit($cfg['linker']['path'])
if $cfg['linker']['options'].nil?
options = ''
else
options = squash('', $cfg['linker']['options'])
end
if ($cfg['linker']['includes'].nil? || $cfg['linker']['includes']['items'].nil?)
includes = ''
else
includes = squash($cfg['linker']['includes']['prefix'], $cfg['linker']['includes']['items'])
end
includes = includes.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR)
return {:command => command, :options => options, :includes => includes}
end
def link_it(exe_name, obj_list)
linker = build_linker_fields
cmd_str = "#{linker[:command]}#{linker[:options]}#{linker[:includes]} " +
(obj_list.map{|obj|"#{$cfg['linker']['object_files']['path']}#{obj} "}).join +
$cfg['linker']['bin_files']['prefix'] + ' ' +
$cfg['linker']['bin_files']['destination'] +
exe_name + $cfg['linker']['bin_files']['extension']
execute(cmd_str)
end
def build_simulator_fields
return nil if $cfg['simulator'].nil?
if $cfg['simulator']['path'].nil?
command = ''
else
command = (tackit($cfg['simulator']['path']) + ' ')
end
if $cfg['simulator']['pre_support'].nil?
pre_support = ''
else
pre_support = squash('', $cfg['simulator']['pre_support'])
end
if $cfg['simulator']['post_support'].nil?
post_support = ''
else
post_support = squash('', $cfg['simulator']['post_support'])
end
return {:command => command, :pre_support => pre_support, :post_support => post_support}
end
def execute(command_string, verbose=true, raise_on_fail=true)
report command_string
output = `#{command_string}`.chomp
report(output) if (verbose && !output.nil? && (output.length > 0))
if (($?.exitstatus != 0) and (raise_on_fail))
raise "Command failed. (Returned #{$?.exitstatus})"
end
return output
end
def report_summary
summary = UnityTestSummary.new
summary.set_root_path(HERE)
results_glob = "#{$cfg['compiler']['build_path']}*.test*"
results_glob.gsub!(/\\/, '/')
results = Dir[results_glob]
summary.set_targets(results)
summary.run
fail_out "FAIL: There were failures" if (summary.failures > 0)
end
def run_tests(test_files)
report 'Running system tests...'
# Tack on TEST define for compiling unit tests
load_configuration($cfg_file)
test_defines = ['TEST']
$cfg['compiler']['defines']['items'] = [] if $cfg['compiler']['defines']['items'].nil?
$cfg['compiler']['defines']['items'] << 'TEST'
include_dirs = get_local_include_dirs
# Build and execute each unit test
test_files.each do |test|
obj_list = []
# Detect dependencies and build required required modules
extract_headers(test).each do |header|
# Compile corresponding source file if it exists
src_file = find_source_file(header, include_dirs)
if !src_file.nil?
obj_list << compile(src_file, test_defines)
end
end
# Build the test runner (generate if configured to do so)
test_base = File.basename(test, C_EXTENSION)
runner_name = test_base + '_Runner.c'
if $cfg['compiler']['runner_path'].nil?
runner_path = $cfg['compiler']['build_path'] + runner_name
test_gen = UnityTestRunnerGenerator.new($cfg_file)
test_gen.run(test, runner_path)
else
runner_path = $cfg['compiler']['runner_path'] + runner_name
end
obj_list << compile(runner_path, test_defines)
# Build the test module
obj_list << compile(test, test_defines)
# Link the test executable
link_it(test_base, obj_list)
# Execute unit test and generate results file
simulator = build_simulator_fields
executable = $cfg['linker']['bin_files']['destination'] + test_base + $cfg['linker']['bin_files']['extension']
if simulator.nil?
cmd_str = executable
else
cmd_str = "#{simulator[:command]} #{simulator[:pre_support]} #{executable} #{simulator[:post_support]}"
end
output = execute(cmd_str, true, false)
test_results = $cfg['compiler']['build_path'] + test_base
if output.match(/OK$/m).nil?
test_results += '.testfail'
else
test_results += '.testpass'
end
File.open(test_results, 'w') { |f| f.print output }
end
end
def build_application(main)
report "Building application..."
obj_list = []
load_configuration($cfg_file)
main_path = $cfg['compiler']['source_path'] + main + C_EXTENSION
# Detect dependencies and build required required modules
include_dirs = get_local_include_dirs
extract_headers(main_path).each do |header|
src_file = find_source_file(header, include_dirs)
if !src_file.nil?
obj_list << compile(src_file)
end
end
# Build the main source file
main_base = File.basename(main_path, C_EXTENSION)
obj_list << compile(main_path)
# Create the executable
link_it(main_base, obj_list)
end
def fail_out(msg)
puts msg
puts "Not returning exit code so continuous integration can pass"
# exit(-1) # Only removed to pass example_3, which has failing tests on purpose.
# Still fail if the build fails for any other reason.
end
end

View File

@@ -0,0 +1,13 @@
Example 3
=========
This example project gives an example of some passing, ignored, and failing tests.
It's simple and meant for you to look over and get an idea for what all of this stuff does.
You can build and test using rake. The rake version will let you test with gcc or a couple
versions of IAR. You can tweak the yaml files to get those versions running.
Ruby is required if you're using the rake version (obviously). This version shows off most of
Unity's advanced features (automatically creating test runners, fancy summaries, etc.)
Without ruby, you have to maintain your own test runners. Do that for a while and you'll learn
why you really want to start using the Ruby tools.

View 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;
}

View File

@@ -0,0 +1,3 @@
int FindFunction_WhichIsBroken(int NumberToFind);
int FunctionWhichReturnsLocalVariable(void);

View 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;
}

View File

@@ -0,0 +1,2 @@
char* ThisFunctionHasNotBeenTested(int Poor, char* LittleFunction);

View File

@@ -0,0 +1,46 @@
# Copied from ~Unity/targets/gcc_32.yml
unity_root: &unity_root '../..'
compiler:
path: gcc
source_path: 'src/'
unit_tests_path: &unit_tests_path 'test/'
build_path: &build_path 'build/'
options:
- '-c'
- '-m32'
- '-Wall'
- '-Wno-address'
- '-std=c99'
- '-pedantic'
includes:
prefix: '-I'
items:
- 'src/'
- '../../src/'
- *unit_tests_path
defines:
prefix: '-D'
items:
- UNITY_INCLUDE_DOUBLE
- UNITY_SUPPORT_TEST_CASES
object_files:
prefix: '-o'
extension: '.o'
destination: *build_path
linker:
path: gcc
options:
- -lm
- '-m32'
includes:
prefix: '-I'
object_files:
path: *build_path
extension: '.o'
bin_files:
prefix: '-o'
extension: '.exe'
destination: *build_path
colour: true
:unity:
:plugins: []

View 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());
}

View 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
}

View File

@@ -0,0 +1,237 @@
/* Unity Configuration
* As of May 11th, 2016 at ThrowTheSwitch/Unity commit 837c529
* Update: December 29th, 2016
* See Also: Unity/docs/UnityConfigurationGuide.pdf
*
* Unity is designed to run on almost anything that is targeted by a C compiler.
* It would be awesome if this could be done with zero configuration. While
* there are some targets that come close to this dream, it is sadly not
* universal. It is likely that you are going to need at least a couple of the
* configuration options described in this document.
*
* All of Unity's configuration options are `#defines`. Most of these are simple
* definitions. A couple are macros with arguments. They live inside the
* unity_internals.h header file. We don't necessarily recommend opening that
* file unless you really need to. That file is proof that a cross-platform
* library is challenging to build. From a more positive perspective, it is also
* proof that a great deal of complexity can be centralized primarily to one
* place in order to provide a more consistent and simple experience elsewhere.
*
* Using These Options
* It doesn't matter if you're using a target-specific compiler and a simulator
* or a native compiler. In either case, you've got a couple choices for
* configuring these options:
*
* 1. Because these options are specified via C defines, you can pass most of
* these options to your compiler through command line compiler flags. Even
* if you're using an embedded target that forces you to use their
* overbearing IDE for all configuration, there will be a place somewhere in
* your project to configure defines for your compiler.
* 2. You can create a custom `unity_config.h` configuration file (present in
* your toolchain's search paths). In this file, you will list definitions
* and macros specific to your target. All you must do is define
* `UNITY_INCLUDE_CONFIG_H` and Unity will rely on `unity_config.h` for any
* further definitions it may need.
*/
#ifndef UNITY_CONFIG_H
#define UNITY_CONFIG_H
/* ************************* AUTOMATIC INTEGER TYPES ***************************
* C's concept of an integer varies from target to target. The C Standard has
* rules about the `int` matching the register size of the target
* microprocessor. It has rules about the `int` and how its size relates to
* other integer types. An `int` on one target might be 16 bits while on another
* target it might be 64. There are more specific types in compilers compliant
* with C99 or later, but that's certainly not every compiler you are likely to
* encounter. Therefore, Unity has a number of features for helping to adjust
* itself to match your required integer sizes. It starts off by trying to do it
* automatically.
**************************************************************************** */
/* The first attempt to guess your types is to check `limits.h`. Some compilers
* that don't support `stdint.h` could include `limits.h`. If you don't
* want Unity to check this file, define this to make it skip the inclusion.
* Unity looks at UINT_MAX & ULONG_MAX, which were available since C89.
*/
/* #define UNITY_EXCLUDE_LIMITS_H */
/* The second thing that Unity does to guess your types is check `stdint.h`.
* This file defines `UINTPTR_MAX`, since C99, that Unity can make use of to
* learn about your system. It's possible you don't want it to do this or it's
* possible that your system doesn't support `stdint.h`. If that's the case,
* you're going to want to define this. That way, Unity will know to skip the
* inclusion of this file and you won't be left with a compiler error.
*/
/* #define UNITY_EXCLUDE_STDINT_H */
/* ********************** MANUAL INTEGER TYPE DEFINITION ***********************
* If you've disabled all of the automatic options above, you're going to have
* to do the configuration yourself. There are just a handful of defines that
* you are going to specify if you don't like the defaults.
**************************************************************************** */
/* Define this to be the number of bits an `int` takes up on your system. The
* default, if not auto-detected, is 32 bits.
*
* Example:
*/
/* #define UNITY_INT_WIDTH 16 */
/* Define this to be the number of bits a `long` takes up on your system. The
* default, if not autodetected, is 32 bits. This is used to figure out what
* kind of 64-bit support your system can handle. Does it need to specify a
* `long` or a `long long` to get a 64-bit value. On 16-bit systems, this option
* is going to be ignored.
*
* Example:
*/
/* #define UNITY_LONG_WIDTH 16 */
/* Define this to be the number of bits a pointer takes up on your system. The
* default, if not autodetected, is 32-bits. If you're getting ugly compiler
* warnings about casting from pointers, this is the one to look at.
*
* Example:
*/
/* #define UNITY_POINTER_WIDTH 64 */
/* Unity will automatically include 64-bit support if it auto-detects it, or if
* your `int`, `long`, or pointer widths are greater than 32-bits. Define this
* to enable 64-bit support if none of the other options already did it for you.
* There can be a significant size and speed impact to enabling 64-bit support
* on small targets, so don't define it if you don't need it.
*/
/* #define UNITY_INCLUDE_64 */
/* *************************** FLOATING POINT TYPES ****************************
* In the embedded world, it's not uncommon for targets to have no support for
* floating point operations at all or to have support that is limited to only
* single precision. We are able to guess integer sizes on the fly because
* integers are always available in at least one size. Floating point, on the
* other hand, is sometimes not available at all. Trying to include `float.h` on
* these platforms would result in an error. This leaves manual configuration as
* the only option.
**************************************************************************** */
/* By default, Unity guesses that you will want single precision floating point
* support, but not double precision. It's easy to change either of these using
* the include and exclude options here. You may include neither, just float,
* or both, as suits your needs.
*/
/* #define UNITY_EXCLUDE_FLOAT */
#define UNITY_INCLUDE_DOUBLE
/* #define UNITY_EXCLUDE_DOUBLE */
/* For features that are enabled, the following floating point options also
* become available.
*/
/* Unity aims for as small of a footprint as possible and avoids most standard
* library calls (some embedded platforms don't have a standard library!).
* Because of this, its routines for printing integer values are minimalist and
* hand-coded. To keep Unity universal, though, we eventually chose to develop
* our own floating point print routines. Still, the display of floating point
* values during a failure are optional. By default, Unity will print the
* actual results of floating point assertion failures. So a failed assertion
* will produce a message like "Expected 4.0 Was 4.25". If you would like less
* verbose failure messages for floating point assertions, use this option to
* give a failure message `"Values Not Within Delta"` and trim the binary size.
*/
/* #define UNITY_EXCLUDE_FLOAT_PRINT */
/* If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C
* floats. If your compiler supports a specialty floating point type, you can
* always override this behavior by using this definition.
*
* Example:
*/
/* #define UNITY_FLOAT_TYPE float16_t */
/* If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard
* C doubles. If you would like to change this, you can specify something else
* by using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long
* double` could enable gargantuan floating point types on your 64-bit processor
* instead of the standard `double`.
*
* Example:
*/
/* #define UNITY_DOUBLE_TYPE long double */
/* If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as
* documented in the Unity Assertion Guide, you will learn that they are not
* really asserting that two values are equal but rather that two values are
* "close enough" to equal. "Close enough" is controlled by these precision
* configuration options. If you are working with 32-bit floats and/or 64-bit
* doubles (the normal on most processors), you should have no need to change
* these options. They are both set to give you approximately 1 significant bit
* in either direction. The float precision is 0.00001 while the double is
* 10^-12. For further details on how this works, see the appendix of the Unity
* Assertion Guide.
*
* Example:
*/
/* #define UNITY_FLOAT_PRECISION 0.001f */
/* #define UNITY_DOUBLE_PRECISION 0.001f */
/* *************************** TOOLSET CUSTOMIZATION ***************************
* In addition to the options listed above, there are a number of other options
* which will come in handy to customize Unity's behavior for your specific
* toolchain. It is possible that you may not need to touch any of these but
* certain platforms, particularly those running in simulators, may need to jump
* through extra hoops to operate properly. These macros will help in those
* situations.
**************************************************************************** */
/* By default, Unity prints its results to `stdout` as it runs. This works
* perfectly fine in most situations where you are using a native compiler for
* testing. It works on some simulators as well so long as they have `stdout`
* routed back to the command line. There are times, however, where the
* simulator will lack support for dumping results or you will want to route
* results elsewhere for other reasons. In these cases, you should define the
* `UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time
* (as an `int`, since this is the parameter type of the standard C `putchar`
* function most commonly used). You may replace this with whatever function
* call you like.
*
* Example:
* Say you are forced to run your test suite on an embedded processor with no
* `stdout` option. You decide to route your test result output to a custom
* serial `RS232_putc()` function you wrote like thus:
*/
/* #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) */
/* #define UNITY_OUTPUT_FLUSH() RS232_flush() */
/* #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) */
/* #define UNITY_OUTPUT_COMPLETE() RS232_close() */
/* For some targets, Unity can make the otherwise required `setUp()` and
* `tearDown()` functions optional. This is a nice convenience for test writers
* since `setUp` and `tearDown` don't often actually _do_ anything. If you're
* using gcc or clang, this option is automatically defined for you. Other
* compilers can also support this behavior, if they support a C feature called
* weak functions. A weak function is a function that is compiled into your
* executable _unless_ a non-weak version of the same function is defined
* elsewhere. If a non-weak version is found, the weak version is ignored as if
* it never existed. If your compiler supports this feature, you can let Unity
* know by defining `UNITY_SUPPORT_WEAK` as the function attributes that would
* need to be applied to identify a function as weak. If your compiler lacks
* support for weak functions, you will always need to define `setUp` and
* `tearDown` functions (though they can be and often will be just empty). The
* most common options for this feature are:
*/
/* #define UNITY_SUPPORT_WEAK weak */
/* #define UNITY_SUPPORT_WEAK __attribute__((weak)) */
/* #define UNITY_NO_WEAK */
/* Some compilers require a custom attribute to be assigned to pointers, like
* `near` or `far`. In these cases, you can give Unity a safe default for these
* by defining this option with the attribute you would like.
*
* Example:
*/
/* #define UNITY_PTR_ATTRIBUTE __attribute__((far)) */
/* #define UNITY_PTR_ATTRIBUTE near */
#endif /* UNITY_CONFIG_H */

View File

@@ -0,0 +1,26 @@
Eclipse error parsers
=====================
These are a godsend for extracting & quickly navigating to
warnings & error messages from console output. Unforunately
I don't know how to write an Eclipse plugin so you'll have
to add them manually.
To add a console parser to Eclipse, go to Window --> Preferences
--> C/C++ --> Build --> Settings. Click on the 'Error Parsers'
tab and then click the 'Add...' button. See the table below for
the parser fields to add.
Eclipse will only parse the console output during a build, so
running your unit tests must be part of your build process.
Either add this to your make/rakefile, or add it as a post-
build step in your Eclipse project settings.
Unity unit test error parsers
-----------------------------
Severity Pattern File Line Description
-------------------------------------------------------------------------------
Error (\.+)(.*?):(\d+):(.*?):FAIL: (.*) $2 $3 $5
Warning (\.+)(.*?):(\d+):(.*?):IGNORE: (.*) $2 $3 $5
Warning (\.+)(.*?):(\d+):(.*?):IGNORE\s*$ $2 $3 Ignored test

View File

@@ -0,0 +1,48 @@
# ==========================================
# 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]
# ==========================================
HERE = File.expand_path(File.dirname(__FILE__)) + '/'
require 'rake'
require 'rake/clean'
require 'rake/testtask'
require HERE + 'rakefile_helper'
TEMP_DIRS = [
File.join(HERE, 'build')
]
TEMP_DIRS.each do |dir|
directory(dir)
CLOBBER.include(dir)
end
task :prepare_for_tests => TEMP_DIRS
include RakefileHelpers
# Load default configuration, for now
DEFAULT_CONFIG_FILE = 'gcc_auto_stdint.yml'
configure_toolchain(DEFAULT_CONFIG_FILE)
task :unit => [:prepare_for_tests] do
run_tests
end
desc "Build and test Unity Framework"
task :all => [:clean, :unit]
task :default => [:clobber, :all]
task :ci => [:no_color, :default]
task :cruise => [:no_color, :default]
desc "Load configuration"
task :config, :config_file do |t, args|
configure_toolchain(args[:config_file])
end
task :no_color do
$colour_output = false
end

Some files were not shown because too many files have changed in this diff Show More