Compare commits

...

210 Commits

Author SHA1 Message Date
Max Bruckner
3a20692c18 Release version 1.4.5 2017-03-28 17:39:39 +02:00
Max Bruckner
2f65e80a34 Fix internal compiler error in GCC on x86 2017-03-28 17:32:20 +02:00
Max Bruckner
ef34500693 cJSON_SetNumberHelper: Fix valueint, closes #138
Thanks @mmkeeper
2017-03-28 17:29:52 +02:00
Max Bruckner
b0dfcde04c Release Version 1.4.4 2017-03-23 22:13:25 +01:00
Max Bruckner
1934059554 ensure: Validate buffer offset 2017-03-23 22:01:38 +01:00
Max Bruckner
cc84a446be ensure: Fix potential off by one error. 2017-03-23 22:01:16 +01:00
Max Bruckner
e58f7ec027 ensure: Fix potential overflow of size_t
This could only happen if the maximum SIZE_T is not at least 2 times
bigger than INT_MAX. Not sure if this can happen on real systems, but
better be safe then sorry.
2017-03-23 20:26:29 +01:00
Max Bruckner
4bfb880093 cJSON.h: Note about buffer size for cJSON_PrintPreallocated 2017-03-22 16:39:10 +01:00
Max Bruckner
b7ce06224b Release version 1.4.3 2017-03-19 11:05:33 +01:00
Max Bruckner
227d3398d6 Fix the pragmas for Wcast-qual with old gcc versions 2017-03-18 17:52:33 +01:00
Max Bruckner
466eb8e3f8 Fix wconversion with old gcc (e.g. 4.3) 2017-03-18 17:52:04 +01:00
Max Bruckner
4ec6e76ea2 tests: print_number: Fix build on 32bit ppc (and potentially others) 2017-03-18 13:25:18 +01:00
Max Bruckner
a1b37d0abe Release Version 1.4.2 2017-03-16 01:28:23 +01:00
Max Bruckner
3d971db426 README: Mention supported cmake and make versions 2017-03-16 01:25:57 +01:00
Max Bruckner
30e1e7af7c CMake: Support cmake down to 2.8.5 2017-03-16 01:09:26 +01:00
Max Bruckner
76e5296d0d CMake: Fix per target disabling of compiler flags
The compiler flag detection was working incorrectly.
2017-03-16 00:22:53 +01:00
Max Bruckner
c597601cf1 tests: run cJSON_test{,_utils} along with the other tests 2017-03-15 20:11:19 +01:00
Max Bruckner
e3d5798896 Release version 1.4.1 2017-03-15 20:11:19 +01:00
Max Bruckner
cf1842dc6f fix: print_number didn't abort when out of memory 2017-03-15 00:09:45 +01:00
Max Bruckner
030d0c14cc Merge branch 'develop' (Release 1.4.0) 2017-03-03 23:26:52 +01:00
Max Bruckner
02cd3eec37 Update version number to 1.4.0 2017-03-03 23:21:53 +01:00
Max Bruckner
bdb59f09de Add contributing guideline 2017-03-03 22:14:11 +01:00
Max Bruckner
5f783fff11 cJSON_Utils: Add gcc pragma to use default visibility for system headers 2017-03-03 00:45:50 +01:00
Max Bruckner
7969af6434 Merge pull request #124 from DaveGamble/print-bool-return
Print functions: Return boolean values instead of pointers
2017-03-03 00:42:01 +01:00
Max Bruckner
0bb1843925 print_value: return as soon as possible 2017-03-03 00:40:02 +01:00
Max Bruckner
5ea4fad263 print_string: return boolean instead of pointer 2017-03-03 00:33:11 +01:00
Max Bruckner
1749de02f8 print_number: return boolean instead of pointer 2017-03-03 00:26:58 +01:00
Max Bruckner
748f4bfd4f print_object: return boolean instead of pointer 2017-03-03 00:21:34 +01:00
Max Bruckner
bea1d102fd print_array: return boolean instead of pointer 2017-03-03 00:16:54 +01:00
Max Bruckner
d441fa05b3 print_value: return boolean instead of pointer 2017-03-03 00:11:43 +01:00
Max Bruckner
3fe72cf2b8 fuzzing: afl.c: Fix printing usage 2017-03-02 23:57:05 +01:00
Max Bruckner
d8d0ae66d3 print_number: Fix incorrect output pointer 2017-03-02 23:57:01 +01:00
Max Bruckner
9d07917feb utf16_literal_to_utf8: Eliminate Duff's Device
This fixes -Wimplicit-fallthrough warnings with GCC7.
2017-03-02 13:46:31 +01:00
Max Bruckner
ad5abf4c5b Update unity with fixes for compiler warnings 2017-03-01 23:20:30 +01:00
Max Bruckner
2c45ad7816 Squashed 'tests/unity/' changes from 2988e98..1f52255
1f52255 Merge pull request #267 from FSMaxB/fix-wconversion
7bce0b4 Fix warning with ubsan and -Wconversion
b5da224 Merge pull request #266 from FSMaxB/fix-double-promotion
1bf22d3 Fix warnings with -Wdouble-promotion

git-subtree-dir: tests/unity
git-subtree-split: 1f522558a6c4577aa937341bf856ba3b1436768a
2017-03-01 23:20:30 +01:00
Max Bruckner
6405fd15e3 CMake: Set default visibility to hidden and dllexport on windows 2017-03-01 23:19:35 +01:00
Max Bruckner
b44c917be9 tests/common: use CJSON_PUBLIC 2017-03-01 23:16:19 +01:00
Max Bruckner
2d3520e0b9 Use own cJSON_bool boolean type in the header 2017-03-01 22:50:12 +01:00
Max Bruckner
2e118df0cd tests/common.h: Remove unused prototype 2017-03-01 22:47:45 +01:00
Max Bruckner
412f4f7d62 Use CJSON_PUBLIC for typecheck functions 2017-03-01 22:47:45 +01:00
Max Bruckner
0aea75fbda Merge pull request #123 from DaveGamble/trim-numbers
Remove traling zeroes when printing floating point numbers
2017-03-01 18:38:48 +01:00
Max Bruckner
0c0dd4a5b0 tests: test trim_trailing_zeroes 2017-03-01 18:29:01 +01:00
Max Bruckner
dd4cb5400f print_number: Remove unnecessary integer handling 2017-03-01 13:28:27 +01:00
Max Bruckner
1ea72f8260 print_number: Remove trailing zeroes (for doubles) 2017-03-01 13:22:32 +01:00
Max Bruckner
e78bc42362 print_number: Return buffer + offset instead of beginning of the number 2017-03-01 13:00:52 +01:00
Max Bruckner
6f271e511f print_number: Use sprintf's return value
This is used to update the buffer offset and determine success
2017-03-01 12:56:32 +01:00
Max Bruckner
bee069b4e7 Merge pull request #122 from DaveGamble/more-compiler-flags
Add more compiler flags
2017-03-01 12:30:12 +01:00
Max Bruckner
1e0bd24f2c Revert "unity: make it work with -Wconversion"
This reverts commit 12acc57967.
2017-03-01 11:57:07 +01:00
Max Bruckner
5cf56fa4fa Add -Wparentheses compile option 2017-03-01 11:57:07 +01:00
Max Bruckner
3f349a4258 Add -Wdouble-promotion compiler flag 2017-03-01 11:57:07 +01:00
Max Bruckner
40e3781e9b CMake: Disable -Werror for Unity 2017-03-01 11:57:07 +01:00
Max Bruckner
b056d7cb74 Add -Wcomma compiler flag 2017-03-01 11:57:06 +01:00
Max Bruckner
1f422b586a Squashed 'tests/unity/' changes from 1782bab..2988e98
2988e98 Merge pull request #262 from codehearts/patch-2
1732698 Fixed incorrect TEST_PROTECT explanation in readme
3817375 Merge pull request #260 from jeremyhannon/parseUnityFixtureOutputToJUnitFormat
9d5159f Merge pull request #261 from codehearts/patch-1
65ce727 Fixed typo for TEST_PROTECT in readme
4dc04d3 Enhance parseOutput.rb to support Unity fixture output

git-subtree-dir: tests/unity
git-subtree-split: 2988e980fbc2252fa4290b608517d4ae25cd9a46
2017-03-01 09:23:18 +01:00
Max Bruckner
899529e866 Update unity 2017-03-01 09:23:18 +01:00
Max Bruckner
2837aac23e Contributors: Add Mike Jerris 2017-02-28 23:04:29 +01:00
Max Bruckner
c66d95d116 Release bugfix release 1.3.2 2017-02-28 21:43:02 +01:00
Max Bruckner
cb6df3ffad Merge pull request #121 from ffontaine/master
Do not build unity library if tests are disabled
2017-02-28 21:34:29 +01:00
Fabrice Fontaine
695d8a01a9 Do not build unity library if tests are disabled
Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2017-02-28 20:48:04 +01:00
Max Bruckner
024f690289 Merge pull request #116 from mjerris/fschanges
add CJSON_DECLARE macros to support gcc visibility and windows dllexport
2017-02-27 21:51:55 +01:00
Mike Jerris
039b1cc653 add CJSON_PUBLIC macro to public functions to support visibility and dllimport/dllexport 2017-02-27 13:47:06 -06:00
Max Bruckner
c0ff1fef9b Release 1.3.1 2017-02-26 22:04:22 +01:00
Max Bruckner
2302f4f0b2 Merge pull request #120 from DaveGamble/typecheck-functions
Typecheck functions
2017-02-26 22:00:28 +01:00
Max Bruckner
c45dc12fd7 Tests for typecheck functions 2017-02-26 21:54:01 +01:00
Max Bruckner
ed8dc53699 cJSON_Utils: Use new typecheck functions 2017-02-26 21:26:34 +01:00
Max Bruckner
c26d53f0d7 Helper function to check the type of an item
This is necessary, because you can get it wrong if you do it manually.
(when you forget the & 0xFF in the comparison)
2017-02-26 14:30:50 +01:00
Max Bruckner
29f312dd69 Merge pull request #118 from mjerris/parser
prevent read beyond end of buffer when string ends with malformed utf
2017-02-26 13:12:26 +01:00
Mike Jerris
72e6e23523 prevent read beyond end of buffer when string ends with malformed utf 2017-02-23 08:59:25 -06:00
Max Bruckner
501046247d fix clang-sanitizer warnings 2017-02-21 15:08:36 +01:00
Max Bruckner
68cd0d4a11 cJSON.c: Pass allocation functions through internal functions
This is the first step in removing the global allocator functions. Every
internal function now only accesses its locally available set of
allocators.
2017-02-21 14:50:49 +01:00
Max Bruckner
56b819bfbc tests: test cJSON_GetObjectItem and cJSON_GetObjectItemCaseSensitive 2017-02-21 11:17:08 +01:00
Max Bruckner
605422c60a cJSON: New function cJSON_GetObjectItemCaseSensitive 2017-02-21 11:17:08 +01:00
Max Bruckner
cbbcc91498 tests: cJSON_ArrayForEach 2017-02-21 09:17:49 +01:00
Max Bruckner
b47d0e34ca cJSON_ArrayForEach: Fix handling of NULL pointers 2017-02-21 09:17:34 +01:00
Max Bruckner
bc12c69b54 Merge pull request #114 from DaveGamble/simplify-print
Simplify print functions
2017-02-20 23:09:06 +01:00
Max Bruckner
1d42c9bc60 print_object: simplify code 2017-02-20 23:07:24 +01:00
Max Bruckner
b71db93e03 print_object: rename variables 2017-02-20 23:07:24 +01:00
Max Bruckner
6d5a7c8c40 print_array: simplify code 2017-02-20 23:07:24 +01:00
Max Bruckner
8c1ed3ab82 update: rename to update_offset and change offset directly 2017-02-20 23:07:24 +01:00
Max Bruckner
f16dd7e028 print_array: rename variables 2017-02-20 23:07:24 +01:00
Max Bruckner
08770fc246 print_value: rename variables 2017-02-20 23:07:23 +01:00
Max Bruckner
0ca8587acc print_string_ptr: simplify code 2017-02-20 23:07:23 +01:00
Max Bruckner
88e38d042f tests: print_string: test if NULL is printed as empty string 2017-02-20 23:07:23 +01:00
Max Bruckner
6a746a230a print_string: Add more const 2017-02-20 23:07:23 +01:00
Max Bruckner
1e999352d3 print_string_ptr: rename variables 2017-02-20 23:07:23 +01:00
Max Bruckner
6815d96617 print_number: rename variables 2017-02-20 23:07:22 +01:00
Max Bruckner
61921498d0 print_number: remove special case for 0
Now that printbuffer is used for everything, it's not needed anymore.
2017-02-20 23:07:22 +01:00
Max Bruckner
4758d62fd4 Merge pull request #113 from DaveGamble/printbuffer-only
Always print with printbuffer
2017-02-20 23:06:58 +01:00
Max Bruckner
1bc1a9748a Merge pull request #112 from DaveGamble/print-tests
Add tests for print functions
2017-02-20 11:15:53 +01:00
Max Bruckner
de36476092 tests: print_value 2017-02-20 11:11:02 +01:00
Max Bruckner
983a4cd286 tests: parse_objects: Fix name of test 2017-02-20 11:11:01 +01:00
Max Bruckner
b6abef94ff tests: print_object 2017-02-20 11:11:01 +01:00
Max Bruckner
3d66766231 tests: print_array 2017-02-20 11:11:01 +01:00
Max Bruckner
924122904e tests: print_number 2017-02-20 11:11:01 +01:00
Max Bruckner
87691a86e5 cJSON_SetNumberValue: Fix compiler warning with -Waddress 2017-02-20 11:11:01 +01:00
Max Bruckner
8aa324fdc8 tests: print_string 2017-02-20 11:11:01 +01:00
Max Bruckner
3c4d309f62 ensure: Don't accept empty printbuffers anymore 2017-02-19 04:20:21 +01:00
Max Bruckner
9bf531ca05 Remove printing without buffer 2017-02-19 04:16:57 +01:00
Max Bruckner
80354bdb06 cJSON_Print: Use printbuffer 2017-02-19 04:03:02 +01:00
Max Bruckner
fe2e0956ad Merge pull request #111 from DaveGamble/fuzzing
partial build system integration of the afl fuzzing tool
2017-02-19 02:44:28 +01:00
Max Bruckner
cf71f3d627 fuzzing: script to prepare linux kernel for afl 2017-02-18 13:18:09 +01:00
Max Bruckner
da551c753f fuzzing: Speed up afl using persistent mode (in proccess fuzzing) 2017-02-18 13:18:09 +01:00
Max Bruckner
ae4681b787 fuzzing: use llvm source code instrumentation 2017-02-18 13:18:08 +01:00
Max Bruckner
c5a09a32a9 fuzzing: Fuzz different print methods
This is achieved by encoding the type of function used in the first two
bytes.

First byte:
b: buffered

Second byte:
f: formatted
2017-02-18 13:18:08 +01:00
Max Bruckner
4785070ad3 fuzzing: Fuzz printing as well.
With one big limitation: It can only be fuzzed with what has been
parsed by the library beforehand.
2017-02-18 12:52:16 +01:00
Max Bruckner
0e0cd5bae5 CMake: Add ENABLE_FUZZING and "afl" target 2017-02-18 12:52:16 +01:00
Max Bruckner
44512f643e Merge pull request #110 from DaveGamble/ensure-improvements
ensure improvements
2017-02-18 12:18:18 +01:00
Max Bruckner
331c18d09a ensure: only memcopy what's necessary
We don't need to copy the entire printbuffer, only the part that is
used.
2017-02-18 12:07:17 +01:00
Max Bruckner
4fff92140e ensure: use realloc if possible 2017-02-18 12:07:17 +01:00
Max Bruckner
fc1d4b07df ensure: if printbuffer is null: cJSON_malloc
This allowed for the removal of a lot of if (p) checks.
2017-02-18 11:35:59 +01:00
Max Bruckner
bd073343fa rename skip -> skip_whitespace 2017-02-17 19:37:31 +01:00
Max Bruckner
7c722dca5f CMake: Add target "check" that builds and runs tests 2017-02-17 13:59:26 +01:00
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
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
d19f3ae890 Merge commit '6b9b57be226a505a9c9cdd9ed029f22495ce04ec' as 'tests/unity' 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
Jiri Zouhar
06008b0444 add support to insert raw json 2016-11-14 11:20:10 +01:00
194 changed files with 21122 additions and 1630 deletions

54
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,54 @@
Contribution Guidelines
=======================
Contributions to cJSON are welcome. If you find a bug or want to improve cJSON in another way, pull requests are appreciated.
For bigger changes, in order to avoid wasted effort, please open an issue to discuss the technical details before creating a pull request.
The further sections explain the process in more detail and provides some guidelines on how contributions should look like.
Branches
--------
There are two branches to be aware of, the `master` and the `develop` branch. The `master` branch is reserved for the latest release, so only make pull requests to the `master` branch for small bug- or security fixes (these are usually just a few lines). In all other cases, please make a pull request to the `develop` branch.
Coding Style
------------
The coding style has been discussed in [#24](https://github.com/DaveGamble/cJSON/issues/24). The basics are:
* Use 4 tabs for indentation
* No oneliners (conditions, loops, variable declarations ...)
* Always use parenthesis for control structures
* Don't implicitly rely on operator precedence, use round brackets in expressions. e.g. `(a > b) && (c < d)` instead of `a>b && c<d`
* opening curly braces start in the next line
* use spaces around operators
* lines should not have trailing whitespace
* use spaces between function parameters
* use pronouncable variable names, not just a combination of letters
Example:
```c
/* calculate the new length of the string in a printbuffer and update the offset */
static void update_offset(printbuffer * const buffer)
{
const unsigned char *buffer_pointer = NULL;
if ((buffer == NULL) || (buffer->buffer == NULL))
{
return;
}
buffer_pointer = buffer->buffer + buffer->offset;
buffer->offset += strlen((const char*)buffer_pointer);
}
```
Unit Tests
----------
cJSON uses the [Unity](https://github.com/ThrowTheSwitch/Unity) library for unit tests. The tests are located in the `tests` directory. In order to add a new test, either add it to one of the existing files (if it fits) or add a new C file for the test. That new file has to be added to the list of tests in `tests/CMakeLists.txt`.
All new features have to be covered by unit tests.
Other Notes
-----------
* Internal functions are to be declared static.
* Wrap the return type of external function in the `CJSON_PUBLIC` macro.

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,88 @@
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 2.8.5)
include(GNUInstallDirs)
project(cJSON C)
set(PROJECT_VERSION_MAJOR 1)
set(PROJECT_VERSION_MINOR 1)
set(PROJECT_VERSION_PATCH 0)
set(PROJECT_VERSION_MINOR 4)
set(PROJECT_VERSION_PATCH 5)
set(CJSON_VERSION_SO 1)
set(CJSON_UTILS_VERSION_SO 1)
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
set(custom_compiler_flags)
include(CheckCCompilerFlag)
option(ENABLE_CUSTOM_COMPILER_FLAGS "Enables custom compiler flags for Clang and GCC" ON)
if (ENABLE_CUSTOM_COMPILER_FLAGS)
if(("${CMAKE_C_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang"))
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c89 -pedantic -Wall -Wextra -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wstrict-overflow=2")
endif()
list(APPEND custom_compiler_flags
-std=c89
-pedantic
-Wall
-Wextra
-Werror
-Wstrict-prototypes
-Wwrite-strings
-Wshadow
-Winit-self
-Wcast-align
-Wformat=2
-Wmissing-prototypes
-Wstrict-overflow=2
-Wcast-qual
-Wundef
-Wswitch-default
-Wconversion
-Wc++-compat
-fstack-protector-strong
-Wcomma
-Wdouble-promotion
-Wparentheses
)
endif()
option(ENABLE_SANITIZERS "Enables AddressSanitizer and UndefinedBehaviorSanitizer." OFF)
if (ENABLE_SANITIZERS)
list(APPEND custom_compiler_flags
-fno-omit-frame-pointer
-fsanitize=address
-fsanitize=undefined
-fsanitize=float-divide-by-zero
-fsanitize=float-cast-overflow
-fsanitize-address-use-after-scope
-fsanitize=integer
-01
-fno-sanitize-recover
)
endif()
option(ENABLE_PUBLIC_SYMBOLS "Export library symbols." On)
if (ENABLE_PUBLIC_SYMBOLS)
list(APPEND custom_compiler_flags -fvisibility=hidden)
add_definitions(-DCJSON_EXPORT_SYMBOLS -DCJSON_API_VISIBILITY)
endif()
option(ENABLE_HIDDEN_SYMBOLS "Hide library symbols." Off)
if (ENABLE_HIDDEN_SYMBOLS)
add_definitions(-DCJSON_HIDE_SYMBOLS -UCJSON_API_VISIBILITY)
endif()
# apply custom compiler flags
foreach(compiler_flag ${custom_compiler_flags})
#remove problematic characters
string(REGEX REPLACE "[^a-zA-Z0-9]" "" current_variable ${compiler_flag})
CHECK_C_COMPILER_FLAG(${compiler_flag} "FLAG_SUPPORTED_${current_variable}")
if (FLAG_SUPPORTED_${current_variable})
list(APPEND supported_compiler_flags)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${compiler_flag}")
endif()
endforeach()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${supported_compiler_flags}")
#variables for pkg-config
set(prefix "${CMAKE_INSTALL_PREFIX}")
set(libdir "${CMAKE_INSTALL_LIBDIR}")
@@ -98,13 +162,36 @@ 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}")
add_test(NAME ${TEST_CJSON} COMMAND "${CMAKE_CURRENT_BINARY_DIR}/${TEST_CJSON}")
# Disable -fsanitize=float-divide-by-zero for cJSON_test
if (FLAG_SUPPORTED_fsanitizefloatdividebyzero)
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=float-divide-by-zero")
else()
target_compile_options(${TEST_CJSON} PRIVATE "-fno-sanitize=float-divide-by-zero")
endif()
endif()
if(ENABLE_CJSON_UTILS)
set(TEST_CJSON_UTILS cJSON_test_utils)
add_executable("${TEST_CJSON_UTILS}" test_utils.c)
target_link_libraries("${TEST_CJSON_UTILS}" "${CJSON_UTILS_LIB}")
add_test(NAME ${TEST_CJSON_UTILS} COMMAND "${CMAKE_CURRENT_BINARY_DIR}/${TEST_CJSON_UTILS}")
endif()
#"check" target that automatically builds everything and runs the tests
add_custom_target(check
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
DEPENDS ${TEST_CJSON} ${TEST_CJSON_UTILS})
endif()
add_subdirectory(tests)
add_subdirectory(fuzzing)

View File

@@ -14,16 +14,19 @@ 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
* [Mike Jerris](https://github.com/mjerris)
* Paulo Antonio Alvarez
* [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)

View File

@@ -10,7 +10,7 @@ UTILS_TEST_SRC = cJSON.c cJSON_Utils.c test_utils.c
LDLIBS = -lm
LIBVERSION = 1.1.0
LIBVERSION = 1.4.5
CJSON_SOVERSION = 1
UTILS_SOVERSION = 1
@@ -23,7 +23,7 @@ INSTALL_LIBRARY_PATH = $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH)
INSTALL ?= cp -a
R_CFLAGS = -fPIC -std=c89 -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wstrict-overflow=2 $(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')

View File

@@ -62,7 +62,7 @@ Because the entire library is only one C file and one header file, you can just
cJSON is written in ANSI C (C89) in order to support as many platforms and compilers as possible.
#### CMake
With CMake, cJSON supports a full blown build system. This way you get the most features. With CMake it is recommended to do an out of tree build, meaning the compiled files are put in a directory separate from the source files. So in order to build cJSON with CMake on a Unix platform, make a `build` directory and run CMake inside it.
With CMake, cJSON supports a full blown build system. This way you get the most features. CMake with an equal or higher version than 2.8.5 is supported. With CMake it is recommended to do an out of tree build, meaning the compiled files are put in a directory separate from the source files. So in order to build cJSON with CMake on a Unix platform, make a `build` directory and run CMake inside it.
```
mkdir build
@@ -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,15 +92,13 @@ 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
```
CMake supports a lot of different platforms, not only UNIX Makefiles, but only UNIX Makefiles have been tested. It works on GNU/Linux and has been confirmed to compile on some versions of macOS, Cygwin, FreeBSD, Solaris and OpenIndiana.
#### Makefile
If you don't have CMake available, but still have make. You can use the makefile to build cJSON:
If you don't have CMake available, but still have GNU make. You can use the makefile to build cJSON:
Run this command in the directory with the source code and it will automatically compile static and shared libraries and a little test program.

2528
cJSON.c

File diff suppressed because it is too large Load Diff

156
cJSON.h
View File

@@ -28,9 +28,15 @@ extern "C"
{
#endif
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 4
#define CJSON_VERSION_PATCH 5
#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 +44,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 +61,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;
@@ -71,82 +78,140 @@ typedef struct cJSON_Hooks
void (*free_fn)(void *ptr);
} cJSON_Hooks;
typedef int cJSON_bool;
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
#define __WINDOWS__
#endif
#ifdef __WINDOWS__
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options:
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols
For *nix builds that support visibility attribute, you can define similar behavior by
setting default visibility to hidden by adding
-fvisibility=hidden (for gcc)
or
-xldscope=hidden (for sun cc)
to CFLAGS
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
*/
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type __stdcall
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall
#else
#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall
#endif
#else /* !WIN32 */
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
#else
#define CJSON_PUBLIC(type) type
#endif
#endif
/* returns the version of cJSON as a string */
CJSON_PUBLIC(const char*) cJSON_Version(void);
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(const cJSON *item);
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(const cJSON *item);
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
extern char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with length buf_len. Returns 1 on success and 0 on failure. */
extern int cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const int fmt);
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
/* NOTE: If you are printing numbers, the buffer hat to be 63 bytes bigger then the printed JSON (worst case) */
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(const cJSON *array);
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(const cJSON *array, int item);
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string);
extern int cJSON_HasObjectItem(const cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *object, const char *string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
/* These functions check the type of an item */
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(const int *numbers, int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count);
extern cJSON *cJSON_CreateStringArray(const char **strings, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
extern void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which);
extern void cJSON_DeleteItemFromArray(cJSON *array, int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
/* Update array items. */
extern void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(const cJSON *item, int recurse);
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
extern cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
extern void cJSON_Minify(char *json);
CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
@@ -155,13 +220,16 @@ 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 */
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Macro for iterating over an array */
#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next)
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
#ifdef __cplusplus
}

View File

@@ -1,16 +1,20 @@
#pragma GCC visibility push(default)
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#pragma GCC visibility pop
#include "cJSON_Utils.h"
static char* cJSONUtils_strdup(const char* str)
static unsigned char* cJSONUtils_strdup(const unsigned char* str)
{
size_t len = 0;
char *copy = NULL;
unsigned char *copy = NULL;
len = strlen(str) + 1;
if (!(copy = (char*)malloc(len)))
len = strlen((const char*)str) + 1;
if (!(copy = (unsigned char*)malloc(len)))
{
return NULL;
}
@@ -19,7 +23,7 @@ static char* cJSONUtils_strdup(const char* str)
return copy;
}
static int cJSONUtils_strcasecmp(const char *s1, const char *s2)
static int cJSONUtils_strcasecmp(const unsigned char *s1, const unsigned char *s2)
{
if (!s1)
{
@@ -29,7 +33,7 @@ static int cJSONUtils_strcasecmp(const char *s1, const char *s2)
{
return 1;
}
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)
for(; tolower(*s1) == tolower(*s2); (void)++s1, ++s2)
{
if(*s1 == 0)
{
@@ -37,17 +41,17 @@ static int cJSONUtils_strcasecmp(const char *s1, const char *s2)
}
}
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
return tolower(*s1) - tolower(*s2);
}
/* JSON Pointer implementation: */
static int cJSONUtils_Pstrcasecmp(const char *a, const char *e)
static int cJSONUtils_Pstrcasecmp(const unsigned char *a, const unsigned char *e)
{
if (!a || !e)
{
return (a == e) ? 0 : 1; /* both NULL? */
}
for (; *a && *e && (*e != '/'); a++, e++) /* compare until next '/' */
for (; *a && *e && (*e != '/'); (void)a++, e++) /* compare until next '/' */
{
if (*e == '~')
{
@@ -76,10 +80,10 @@ static int cJSONUtils_Pstrcasecmp(const char *a, const char *e)
return 0;
}
static int cJSONUtils_PointerEncodedstrlen(const char *s)
static size_t cJSONUtils_PointerEncodedstrlen(const unsigned char *s)
{
int l = 0;
for (; *s; s++, l++)
size_t l = 0;
for (; *s; (void)s++, l++)
{
if ((*s == '~') || (*s == '/'))
{
@@ -90,7 +94,7 @@ static int cJSONUtils_PointerEncodedstrlen(const char *s)
return l;
}
static void cJSONUtils_PointerEncodedstrcpy(char *d, const char *s)
static void cJSONUtils_PointerEncodedstrcpy(unsigned char *d, const unsigned char *s)
{
for (; *s; s++)
{
@@ -113,42 +117,49 @@ static void cJSONUtils_PointerEncodedstrcpy(char *d, const char *s)
*d = '\0';
}
char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
{
int type = object->type;
int c = 0;
size_t c = 0;
cJSON *obj = 0;
if (object == target)
{
/* found */
return cJSONUtils_strdup("");
return (char*)cJSONUtils_strdup((const unsigned char*)"");
}
/* recursively search all children of the object */
for (obj = object->child; obj; obj = obj->next, c++)
for (obj = object->child; obj; (void)(obj = obj->next), c++)
{
char *found = cJSONUtils_FindPointerFromObjectTo(obj, target);
unsigned char *found = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(obj, target);
if (found)
{
if ((type & 0xFF) == cJSON_Array)
if (cJSON_IsArray(object))
{
/* reserve enough memory for a 64 bit integer + '/' and '\0' */
char *ret = (char*)malloc(strlen(found) + 23);
sprintf(ret, "/%d%s", c, found); /* /<array_index><path> */
free(found);
return ret;
}
else if ((type & 0xFF) == cJSON_Object)
unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + 23);
/* check if conversion to unsigned long is valid
* This should be eliminated at compile time by dead code elimination
* if size_t is an alias of unsigned long, or if it is bigger */
if (c > ULONG_MAX)
{
char *ret = (char*)malloc(strlen(found) + cJSONUtils_PointerEncodedstrlen(obj->string) + 2);
*ret = '/';
cJSONUtils_PointerEncodedstrcpy(ret + 1, obj->string);
strcat(ret, found);
free(found);
return NULL;
}
sprintf((char*)ret, "/%lu%s", (unsigned long)c, found); /* /<array_index><path> */
free(found);
return ret;
return (char*)ret;
}
else if (cJSON_IsObject(object))
{
unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + cJSONUtils_PointerEncodedstrlen((unsigned char*)obj->string) + 2);
*ret = '/';
cJSONUtils_PointerEncodedstrcpy(ret + 1, (unsigned char*)obj->string);
strcat((char*)ret, (char*)found);
free(found);
return (char*)ret;
}
/* reached leaf of the tree, found nothing */
@@ -161,31 +172,35 @@ char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
return NULL;
}
cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer)
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer)
{
/* follow path of the pointer */
while ((*pointer++ == '/') && object)
{
if ((object->type & 0xFF) == cJSON_Array)
if (cJSON_IsArray(object))
{
int which = 0;
size_t which = 0;
/* parse array index */
while ((*pointer >= '0') && (*pointer <= '9'))
{
which = (10 * which) + (*pointer++ - '0');
which = (10 * which) + (size_t)(*pointer++ - '0');
}
if (*pointer && (*pointer != '/'))
{
/* not end of string or new path token */
return NULL;
}
object = cJSON_GetArrayItem(object, which);
if (which > INT_MAX)
{
return NULL;
}
else if ((object->type & 0xFF) == cJSON_Object)
object = cJSON_GetArrayItem(object, (int)which);
}
else if (cJSON_IsObject(object))
{
object = object->child;
/* GetObjectItem. */
while (object && cJSONUtils_Pstrcasecmp(object->string, pointer))
while (object && cJSONUtils_Pstrcasecmp((unsigned char*)object->string, (const unsigned char*)pointer))
{
object = object->next;
}
@@ -205,37 +220,49 @@ 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;
for (; *string; s2++, string++)
unsigned char *s2 = string;
if (string == NULL) {
return;
}
for (; *string; (void)s2++, string++)
{
*s2 = (*string != '~')
*s2 = (unsigned char) ((*string != '~')
? (*string)
: ((*(++string) == '0')
? '~'
: '/');
: '/'));
}
*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)
if (parentptr == NULL) {
return NULL;
}
childptr = (unsigned char*)strrchr((char*)parentptr, '/'); /* last '/' */
if (childptr == NULL)
{
free(parentptr);
return NULL;
}
/* split strings */
*childptr++ = '\0';
}
parent = cJSONUtils_GetPointer(object, parentptr);
parent = cJSONUtils_GetPointer(object, (char*)parentptr);
cJSONUtils_InplaceDecodePointerString(childptr);
if (!parent)
@@ -243,13 +270,13 @@ static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path)
/* Couldn't find object to remove child from. */
ret = NULL;
}
else if ((parent->type & 0xFF) == cJSON_Array)
else if (cJSON_IsArray(parent))
{
ret = cJSON_DetachItemFromArray(parent, atoi(childptr));
ret = cJSON_DetachItemFromArray(parent, atoi((char*)childptr));
}
else if ((parent->type & 0xFF) == cJSON_Object)
else if (cJSON_IsObject(parent))
{
ret = cJSON_DetachItemFromObject(parent, childptr);
ret = cJSON_DetachItemFromObject(parent, (char*)childptr);
}
free(parentptr);
@@ -259,7 +286,7 @@ static cJSON *cJSONUtils_PatchDetach(cJSON *object, const char *path)
static int cJSONUtils_Compare(cJSON *a, cJSON *b)
{
if ((a->type & 0xFF) != (b->type & 0xFF))
if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
{
/* mismatched type. */
return -1;
@@ -273,7 +300,7 @@ static int cJSONUtils_Compare(cJSON *a, cJSON *b)
/* string mismatch. */
return (strcmp(a->valuestring, b->valuestring) != 0) ? -3 : 0;
case cJSON_Array:
for (a = a->child, b = b->child; a && b; a = a->next, b = b->next)
for ((void)(a = a->child), b = b->child; a && b; (void)(a = a->next), b = b->next)
{
int err = cJSONUtils_Compare(a, b);
if (err)
@@ -292,7 +319,7 @@ static int cJSONUtils_Compare(cJSON *a, cJSON *b)
{
int err = 0;
/* compare object keys */
if (cJSONUtils_strcasecmp(a->string, b->string))
if (cJSONUtils_strcasecmp((unsigned char*)a->string, (unsigned char*)b->string))
{
/* missing member */
return -6;
@@ -322,8 +349,8 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
cJSON *value = NULL;
cJSON *parent = NULL;
int opcode = 0;
char *parentptr = NULL;
char *childptr = NULL;
unsigned char *parentptr = NULL;
unsigned char *childptr = NULL;
op = cJSON_GetObjectItem(patch, "op");
path = cJSON_GetObjectItem(patch, "path");
@@ -369,7 +396,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
if ((opcode == 1) || (opcode == 2))
{
/* Get rid of old. */
cJSON_Delete(cJSONUtils_PatchDetach(object, path->valuestring));
cJSON_Delete(cJSONUtils_PatchDetach(object, (unsigned char*)path->valuestring));
if (opcode == 1)
{
/* For Remove, this is job done. */
@@ -390,7 +417,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
if (opcode == 3)
{
/* move */
value = cJSONUtils_PatchDetach(object, from->valuestring);
value = cJSONUtils_PatchDetach(object, (unsigned char*)from->valuestring);
}
if (opcode == 4)
{
@@ -431,13 +458,13 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
/* Now, just add "value" to "path". */
/* split pointer in parent and child */
parentptr = cJSONUtils_strdup(path->valuestring);
childptr = strrchr(parentptr, '/');
parentptr = cJSONUtils_strdup((unsigned char*)path->valuestring);
childptr = (unsigned char*)strrchr((char*)parentptr, '/');
if (childptr)
{
*childptr++ = '\0';
}
parent = cJSONUtils_GetPointer(object, parentptr);
parent = cJSONUtils_GetPointer(object, (char*)parentptr);
cJSONUtils_InplaceDecodePointerString(childptr);
/* add, remove, replace, move, copy, test. */
@@ -448,21 +475,21 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
cJSON_Delete(value);
return 9;
}
else if ((parent->type & 0xFF) == cJSON_Array)
else if (cJSON_IsArray(parent))
{
if (!strcmp(childptr, "-"))
if (!strcmp((char*)childptr, "-"))
{
cJSON_AddItemToArray(parent, value);
}
else
{
cJSON_InsertItemInArray(parent, atoi(childptr), value);
cJSON_InsertItemInArray(parent, atoi((char*)childptr), value);
}
}
else if ((parent->type & 0xFF) == cJSON_Object)
else if (cJSON_IsObject(parent))
{
cJSON_DeleteItemFromObject(parent, childptr);
cJSON_AddItemToObject(parent, childptr, value);
cJSON_DeleteItemFromObject(parent, (char*)childptr);
cJSON_AddItemToObject(parent, (char*)childptr, value);
}
else
{
@@ -473,10 +500,16 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
return 0;
}
int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
{
int err = 0;
if ((patches->type & 0xFF) != cJSON_Array)
if (patches == NULL)
{
return 1;
}
if (cJSON_IsArray(patches))
{
/* malformed patches. */
return 1;
@@ -497,20 +530,20 @@ int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
return 0;
}
static void cJSONUtils_GeneratePatch(cJSON *patches, const char *op, const char *path, const char *suffix, cJSON *val)
static void cJSONUtils_GeneratePatch(cJSON *patches, const unsigned char *op, const unsigned char *path, const unsigned char *suffix, cJSON *val)
{
cJSON *patch = cJSON_CreateObject();
cJSON_AddItemToObject(patch, "op", cJSON_CreateString(op));
cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)op));
if (suffix)
{
char *newpath = (char*)malloc(strlen(path) + cJSONUtils_PointerEncodedstrlen(suffix) + 2);
cJSONUtils_PointerEncodedstrcpy(newpath + sprintf(newpath, "%s/", path), suffix);
cJSON_AddItemToObject(patch, "path", cJSON_CreateString(newpath));
unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + cJSONUtils_PointerEncodedstrlen(suffix) + 2);
cJSONUtils_PointerEncodedstrcpy(newpath + sprintf((char*)newpath, "%s/", (const char*)path), suffix);
cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)newpath));
free(newpath);
}
else
{
cJSON_AddItemToObject(patch, "path", cJSON_CreateString(path));
cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
}
if (val)
{
@@ -519,16 +552,21 @@ static void cJSONUtils_GeneratePatch(cJSON *patches, const char *op, const char
cJSON_AddItemToArray(patches, patch);
}
void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val)
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val)
{
cJSONUtils_GeneratePatch(array, op, path, 0, val);
cJSONUtils_GeneratePatch(array, (const unsigned char*)op, (const unsigned char*)path, 0, val);
}
static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *from, cJSON *to)
static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path, cJSON *from, cJSON *to)
{
if ((from == NULL) || (to == NULL))
{
return;
}
if ((from->type & 0xFF) != (to->type & 0xFF))
{
cJSONUtils_GeneratePatch(patches, "replace", path, 0, to);
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
return;
}
@@ -537,37 +575,53 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *f
case cJSON_Number:
if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble))
{
cJSONUtils_GeneratePatch(patches, "replace", path, 0, to);
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
}
return;
case cJSON_String:
if (strcmp(from->valuestring, to->valuestring) != 0)
{
cJSONUtils_GeneratePatch(patches, "replace", path, 0, to);
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
}
return;
case cJSON_Array:
{
int c = 0;
char *newpath = (char*)malloc(strlen(path) + 23); /* Allow space for 64bit int. */
size_t c = 0;
unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + 23); /* Allow space for 64bit int. */
/* generate patches for all array elements that exist in "from" and "to" */
for (c = 0, from = from->child, to = to->child; from && to; from = from->next, to = to->next, c++)
for ((void)(c = 0), (void)(from = from->child), to = to->child; from && to; (void)(from = from->next), (void)(to = to->next), c++)
{
sprintf(newpath, "%s/%d", path, c); /* path of the current array element */
/* check if conversion to unsigned long is valid
* This should be eliminated at compile time by dead code elimination
* if size_t is an alias of unsigned long, or if it is bigger */
if (c > ULONG_MAX)
{
free(newpath);
return;
}
sprintf((char*)newpath, "%s/%lu", path, (unsigned long)c); /* path of the current array element */
cJSONUtils_CompareToPatch(patches, newpath, from, to);
}
/* remove leftover elements from 'from' that are not in 'to' */
for (; from; from = from->next, c++)
for (; from; (void)(from = from->next), c++)
{
sprintf(newpath, "%d", c);
cJSONUtils_GeneratePatch(patches, "remove", path, newpath, 0);
/* check if conversion to unsigned long is valid
* This should be eliminated at compile time by dead code elimination
* if size_t is an alias of unsigned long, or if it is bigger */
if (c > ULONG_MAX)
{
free(newpath);
return;
}
sprintf((char*)newpath, "%lu", (unsigned long)c);
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, newpath, 0);
}
/* add new elements in 'to' that were not in 'from' */
for (; to; to = to->next, c++)
for (; to; (void)(to = to->next), c++)
{
cJSONUtils_GeneratePatch(patches, "add", path, "-", to);
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to);
}
free(newpath);
return;
@@ -585,12 +639,12 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *f
/* for all object values in the object with more of them */
while (a || b)
{
int diff = (!a) ? 1 : ((!b) ? -1 : cJSONUtils_strcasecmp(a->string, b->string));
int diff = (!a) ? 1 : ((!b) ? -1 : cJSONUtils_strcasecmp((unsigned char*)a->string, (unsigned char*)b->string));
if (!diff)
{
/* both object keys are the same */
char *newpath = (char*)malloc(strlen(path) + cJSONUtils_PointerEncodedstrlen(a->string) + 2);
cJSONUtils_PointerEncodedstrcpy(newpath + sprintf(newpath, "%s/", path), a->string);
unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + cJSONUtils_PointerEncodedstrlen((unsigned char*)a->string) + 2);
cJSONUtils_PointerEncodedstrcpy(newpath + sprintf((char*)newpath, "%s/", path), (unsigned char*)a->string);
/* create a patch for the element */
cJSONUtils_CompareToPatch(patches, newpath, a, b);
free(newpath);
@@ -600,13 +654,13 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *f
else if (diff < 0)
{
/* object element doesn't exist in 'to' --> remove it */
cJSONUtils_GeneratePatch(patches, "remove", path, a->string, 0);
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, (unsigned char*)a->string, 0);
a = a->next;
}
else
{
/* object element doesn't exist in 'from' --> add it */
cJSONUtils_GeneratePatch(patches, "add", path, b->string, b);
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (unsigned char*)b->string, b);
b = b->next;
}
}
@@ -618,10 +672,10 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *f
}
}
cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to)
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON *from, cJSON *to)
{
cJSON *patches = cJSON_CreateArray();
cJSONUtils_CompareToPatch(patches, "", from, to);
cJSONUtils_CompareToPatch(patches, (const unsigned char*)"", from, to);
return patches;
}
@@ -639,7 +693,7 @@ static cJSON *cJSONUtils_SortList(cJSON *list)
return list;
}
while (ptr && ptr->next && (cJSONUtils_strcasecmp(ptr->string, ptr->next->string) < 0))
while (ptr && ptr->next && (cJSONUtils_strcasecmp((unsigned char*)ptr->string, (unsigned char*)ptr->next->string) < 0))
{
/* Test for list sorted. */
ptr = ptr->next;
@@ -676,7 +730,7 @@ static cJSON *cJSONUtils_SortList(cJSON *list)
while (first && second) /* Merge the sub-lists */
{
if (cJSONUtils_strcasecmp(first->string, second->string) < 0)
if (cJSONUtils_strcasecmp((unsigned char*)first->string, (unsigned char*)second->string) < 0)
{
if (!list)
{
@@ -733,21 +787,21 @@ static cJSON *cJSONUtils_SortList(cJSON *list)
return list;
}
void cJSONUtils_SortObject(cJSON *object)
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON *object)
{
object->child = cJSONUtils_SortList(object->child);
}
cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
{
if (!patch || ((patch->type & 0xFF) != cJSON_Object))
if (!cJSON_IsObject(patch))
{
/* scalar value, array or NULL, just duplicate */
cJSON_Delete(target);
return cJSON_Duplicate(patch, 1);
}
if (!target || ((target->type & 0xFF) != cJSON_Object))
if (!cJSON_IsObject(target))
{
cJSON_Delete(target);
target = cJSON_CreateObject();
@@ -756,7 +810,7 @@ cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
patch = patch->child;
while (patch)
{
if ((patch->type & 0xFF) == cJSON_NULL)
if (cJSON_IsNull(patch))
{
/* NULL is the indicator to remove a value, see RFC7396 */
cJSON_DeleteItemFromObject(target, patch->string);
@@ -771,7 +825,7 @@ cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
return target;
}
cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
{
cJSON *patch = NULL;
if (!to)
@@ -779,7 +833,7 @@ cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
/* patch to delete everything */
return cJSON_CreateNull();
}
if (((to->type & 0xFF) != cJSON_Object) || !from || ((from->type & 0xFF) != cJSON_Object))
if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
{
return cJSON_Duplicate(to, 1);
}

View File

@@ -1,14 +1,14 @@
#include "cJSON.h"
/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer);
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer);
/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to);
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON *from, cJSON *to);
/* Utility for generating patch array entries. */
void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val);
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val);
/* Returns 0 for success. */
int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
/*
// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
@@ -33,12 +33,12 @@ int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
/* target will be modified by patch. return value is new ptr for target. */
cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch);
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch);
/* generates a patch to move from -> to */
cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to);
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to);
/* Given a root object and a target object, construct a pointer from one to the other. */
char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target);
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target);
/* Sorts the members of the object into alphabetical order. */
void cJSONUtils_SortObject(cJSON *object);
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON *object);

1
fuzzing/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
afl-build

28
fuzzing/CMakeLists.txt Normal file
View File

@@ -0,0 +1,28 @@
option(ENABLE_FUZZING "Create executables and targets for fuzzing cJSON with afl." Off)
if (ENABLE_FUZZING)
find_program(AFL_FUZZ afl-fuzz)
if ("${AFL_FUZZ}" MATCHES "AFL_FUZZ-NOTFOUND")
message(FATAL_ERROR "Couldn't find afl-fuzz.")
endif()
add_executable(afl-main afl.c)
target_link_libraries(afl-main "${CJSON_LIB}")
if (NOT ENABLE_SANITIZERS)
message(FATAL_ERROR "Enable sanitizers with -DENABLE_SANITIZERS=On to do fuzzing.")
endif()
option(ENABLE_FUZZING_PRINT "Fuzz printing functions together with parser." On)
set(fuzz_print_parameter "no")
if (ENABLE_FUZZING_PRINT)
set(fuzz_print_parameter "yes")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error")
add_custom_target(afl
COMMAND "${AFL_FUZZ}" -i "${CMAKE_CURRENT_SOURCE_DIR}/inputs" -o "${CMAKE_CURRENT_BINARY_DIR}/findings" -x "${CMAKE_CURRENT_SOURCE_DIR}/json.dict" -- "${CMAKE_CURRENT_BINARY_DIR}/afl-main" "@@" "${fuzz_print_parameter}"
DEPENDS afl-main)
endif()

5
fuzzing/afl-prepare-linux.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
set -x
echo core | sudo tee /proc/sys/kernel/core_pattern
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

176
fuzzing/afl.c Normal file
View File

@@ -0,0 +1,176 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../cJSON.h"
static char *read_file(const char *filename)
{
FILE *file = NULL;
long length = 0;
char *content = NULL;
size_t read_chars = 0;
/* open in read binary mode */
file = fopen(filename, "rb");
if (file == NULL)
{
goto cleanup;
}
/* get the length */
if (fseek(file, 0, SEEK_END) != 0)
{
goto cleanup;
}
length = ftell(file);
if (length < 0)
{
goto cleanup;
}
if (fseek(file, 0, SEEK_SET) != 0)
{
goto cleanup;
}
/* allocate content buffer */
content = (char*)malloc((size_t)length + sizeof('\0'));
if (content == NULL)
{
goto cleanup;
}
/* read the file into memory */
read_chars = fread(content, sizeof(char), (size_t)length, file);
if ((long)read_chars != length)
{
free(content);
content = NULL;
goto cleanup;
}
content[read_chars] = '\0';
cleanup:
if (file != NULL)
{
fclose(file);
}
return content;
}
int main(int argc, char** argv)
{
const char *filename = NULL;
cJSON *item = NULL;
char *json = NULL;
int status = EXIT_FAILURE;
char *printed_json = NULL;
if ((argc < 2) || (argc > 3))
{
printf("Usage:\n");
printf("%s input_file [enable_printing]\n", argv[0]);
printf("\t input_file: file containing the test data\n");
printf("\t enable_printing: print after parsing, 'yes' or 'no', defaults to 'no'\n");
goto cleanup;
}
filename = argv[1];
#if __AFL_HAVE_MANUAL_CONTROL
while (__AFL_LOOP(1000))
{
#endif
status = EXIT_SUCCESS;
json = read_file(filename);
if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0'))
{
status = EXIT_FAILURE;
goto cleanup;
}
item = cJSON_Parse(json + 2);
if (item == NULL)
{
goto cleanup;
}
if ((argc == 3) && (strncmp(argv[2], "yes", 3) == 0))
{
int do_format = 0;
if (json[1] == 'f')
{
do_format = 1;
}
if (json[0] == 'b')
{
/* buffered printing */
printed_json = cJSON_PrintBuffered(item, 1, do_format);
}
else
{
/* unbuffered printing */
if (do_format)
{
printed_json = cJSON_Print(item);
}
else
{
printed_json = cJSON_PrintUnformatted(item);
}
}
if (printed_json == NULL)
{
status = EXIT_FAILURE;
goto cleanup;
}
printf("%s\n", printed_json);
}
cleanup:
if (item != NULL)
{
cJSON_Delete(item);
item = NULL;
}
if (json != NULL)
{
free(json);
json = NULL;
}
if (printed_json != NULL)
{
free(printed_json);
printed_json = NULL;
}
#if __AFL_HAVE_MANUAL_CONTROL
}
#endif
return status;
}

9
fuzzing/afl.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
mkdir -p afl-build || exit 1
cd afl-build || exit 1
#cleanup
rm -r -- *
CC=afl-clang-fast cmake ../.. -DENABLE_FUZZING=On -DENABLE_SANITIZERS=On -DBUILD_SHARED_LIBS=Off
make afl

22
fuzzing/inputs/test1 Normal file
View File

@@ -0,0 +1,22 @@
bf{
"glossary": {
"title": "example glossary",
"GlossDiv": {
"title": "S",
"GlossList": {
"GlossEntry": {
"ID": "SGML",
"SortAs": "SGML",
"GlossTerm": "Standard Generalized Markup Language",
"Acronym": "SGML",
"Abbrev": "ISO 8879:1986",
"GlossDef": {
"para": "A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso": ["GML", "XML"]
},
"GlossSee": "markup"
}
}
}
}
}

1
fuzzing/inputs/test10 Normal file
View File

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

8
fuzzing/inputs/test11 Normal file
View File

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

11
fuzzing/inputs/test2 Normal file
View File

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

26
fuzzing/inputs/test3 Normal file
View File

@@ -0,0 +1,26 @@
bf{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

26
fuzzing/inputs/test3.bu Normal file
View File

@@ -0,0 +1,26 @@
bu{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

26
fuzzing/inputs/test3.uf Normal file
View File

@@ -0,0 +1,26 @@
uf{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

26
fuzzing/inputs/test3.uu Normal file
View File

@@ -0,0 +1,26 @@
uu{"widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"name": "sun1",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"name": "text1",
"hOffset": 250,
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}}

88
fuzzing/inputs/test4 Normal file
View File

@@ -0,0 +1,88 @@
bf{"web-app": {
"servlet": [
{
"servlet-name": "cofaxCDS",
"servlet-class": "org.cofax.cds.CDSServlet",
"init-param": {
"configGlossary:installationAt": "Philadelphia, PA",
"configGlossary:adminEmail": "ksm@pobox.com",
"configGlossary:poweredBy": "Cofax",
"configGlossary:poweredByIcon": "/images/cofax.gif",
"configGlossary:staticPath": "/content/static",
"templateProcessorClass": "org.cofax.WysiwygTemplate",
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
"templatePath": "templates",
"templateOverridePath": "",
"defaultListTemplate": "listTemplate.htm",
"defaultFileTemplate": "articleTemplate.htm",
"useJSP": false,
"jspListTemplate": "listTemplate.jsp",
"jspFileTemplate": "articleTemplate.jsp",
"cachePackageTagsTrack": 200,
"cachePackageTagsStore": 200,
"cachePackageTagsRefresh": 60,
"cacheTemplatesTrack": 100,
"cacheTemplatesStore": 50,
"cacheTemplatesRefresh": 15,
"cachePagesTrack": 200,
"cachePagesStore": 100,
"cachePagesRefresh": 10,
"cachePagesDirtyRead": 10,
"searchEngineListTemplate": "forSearchEnginesList.htm",
"searchEngineFileTemplate": "forSearchEngines.htm",
"searchEngineRobotsDb": "WEB-INF/robots.db",
"useDataStore": true,
"dataStoreClass": "org.cofax.SqlDataStore",
"redirectionClass": "org.cofax.SqlRedirection",
"dataStoreName": "cofax",
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
"dataStoreUser": "sa",
"dataStorePassword": "dataStoreTestQuery",
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
"dataStoreInitConns": 10,
"dataStoreMaxConns": 100,
"dataStoreConnUsageLimit": 100,
"dataStoreLogLevel": "debug",
"maxUrlLength": 500}},
{
"servlet-name": "cofaxEmail",
"servlet-class": "org.cofax.cds.EmailServlet",
"init-param": {
"mailHost": "mail1",
"mailHostOverride": "mail2"}},
{
"servlet-name": "cofaxAdmin",
"servlet-class": "org.cofax.cds.AdminServlet"},
{
"servlet-name": "fileServlet",
"servlet-class": "org.cofax.cds.FileServlet"},
{
"servlet-name": "cofaxTools",
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
"init-param": {
"templatePath": "toolstemplates/",
"log": 1,
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
"logMaxSize": "",
"dataLog": 1,
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
"dataLogMaxSize": "",
"removePageCache": "/content/admin/remove?cache=pages&id=",
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
"lookInContext": 1,
"adminGroupID": 4,
"betaServer": true}}],
"servlet-mapping": {
"cofaxCDS": "/",
"cofaxEmail": "/cofaxutil/aemail/*",
"cofaxAdmin": "/admin/*",
"fileServlet": "/static/*",
"cofaxTools": "/tools/*"},
"taglib": {
"taglib-uri": "cofax.tld",
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}

27
fuzzing/inputs/test5 Normal file
View File

@@ -0,0 +1,27 @@
bf{"menu": {
"header": "SVG Viewer",
"items": [
{"id": "Open"},
{"id": "OpenNew", "label": "Open New"},
null,
{"id": "ZoomIn", "label": "Zoom In"},
{"id": "ZoomOut", "label": "Zoom Out"},
{"id": "OriginalView", "label": "Original View"},
null,
{"id": "Quality"},
{"id": "Pause"},
{"id": "Mute"},
null,
{"id": "Find", "label": "Find..."},
{"id": "FindAgain", "label": "Find Again"},
{"id": "Copy"},
{"id": "CopyAgain", "label": "Copy Again"},
{"id": "CopySVG", "label": "Copy SVG"},
{"id": "ViewSVG", "label": "View SVG"},
{"id": "ViewSource", "label": "View Source"},
{"id": "SaveAs", "label": "Save As"},
null,
{"id": "Help"},
{"id": "About", "label": "About Adobe CVG Viewer..."}
]
}}

16
fuzzing/inputs/test6 Normal file
View File

@@ -0,0 +1,16 @@
bf<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
html, body, iframe { margin: 0; padding: 0; height: 100%; }
iframe { display: block; width: 100%; border: none; }
</style>
<title>Application Error</title>
</head>
<body>
<iframe src="//s3.amazonaws.com/heroku_pages/error.html">
<p>Application Error</p>
</iframe>
</body>
</html>

22
fuzzing/inputs/test7 Normal file
View File

@@ -0,0 +1,22 @@
bf[
{
"precision": "zip",
"Latitude": 37.7668,
"Longitude": -122.3959,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
"Zip": "94107",
"Country": "US"
},
{
"precision": "zip",
"Latitude": 37.371991,
"Longitude": -122.026020,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
"Zip": "94085",
"Country": "US"
}
]

13
fuzzing/inputs/test8 Normal file
View File

@@ -0,0 +1,13 @@
bf{
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor",
"Thumbnail": {
"Url": "http:/*www.example.com/image/481989943",
"Height": 125,
"Width": "100"
},
"IDs": [116, 943, 234, 38793]
}
}

5
fuzzing/inputs/test9 Normal file
View File

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

47
fuzzing/json.dict Normal file
View File

@@ -0,0 +1,47 @@
#
# AFL dictionary for JSON
# -----------------------------
#
object_start="{"
object_end="}"
object_empty="{}"
object_one_element="{\"one\":1}"
object_two_elements="{\"1\":1,\"2\":2}"
object_separator=":"
array_start="["
array_end="]"
array_empty="[]"
array_one_element="[1]"
array_two_elements="[1,2]"
separator=","
escape_sequence_b="\\b"
escape_sequence_f="\\f"
escape_sequence_n="\\n"
escape_sequence_r="\\r"
escape_sequence_t="\\t"
escape_sequence_quote="\\\""
escape_sequence_backslash="\\\\"
escapce_sequence_slash="\\/"
escpae_sequence_utf16_base="\\u"
escape_sequence_utf16="\\u12ab"
number_integer="1"
number_double="1.0"
number_negative_integer="-1"
number_negative_double="-1.0"
number_engineering1="1e1"
number_engineering2="1e-1"
number_positive_integer="+1"
number_positive_double="+1.0"
number_e="e"
number_plus="+"
number_minus="-"
number_separator="."
null="null"
true="true"
false="false"

153
test.c
View File

@@ -25,57 +25,6 @@
#include <string.h>
#include "cJSON.h"
/* Parse text to JSON, then render back to text, and print! */
static void doit(char *text)
{
char *out = NULL;
cJSON *json = NULL;
json = cJSON_Parse(text);
if (!json)
{
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
}
else
{
out = cJSON_Print(json);
cJSON_Delete(json);
printf("%s\n", out);
free(out);
}
}
#if 0
/* Read a file, parse, render back, etc. */
static void dofile(char *filename)
{
FILE *f = NULL;
long len = 0;
char *data = NULL;
/* open in read binary mode */
f = fopen(filename,"rb");
/* get the length */
fseek(f, 0, SEEK_END);
len = ftell(f);
fseek(f, 0, SEEK_SET);
data = (char*)malloc(len + 1);
if (data == NULL)
{
printf("Failed to allocate memory.\n");
exit(1);
}
fread(data, 1, len, f);
data[len] = '\0';
fclose(f);
doit(data);
free(data);
}
#endif
/* Used by some code below as an example datatype. */
struct record
{
@@ -97,8 +46,8 @@ static int print_preallocated(cJSON *root)
char *out = NULL;
char *buf = NULL;
char *buf_fail = NULL;
int len = 0;
int len_fail = 0;
size_t len = 0;
size_t len_fail = 0;
/* formatted print */
out = cJSON_Print(root);
@@ -106,7 +55,7 @@ static int print_preallocated(cJSON *root)
/* create buffer to succeed */
/* the extra 64 bytes are in case a floating point value is printed */
len = strlen(out) + 64;
buf = malloc(len);
buf = (char*)malloc(len);
if (buf == NULL)
{
printf("Failed to allocate memory.\n");
@@ -115,7 +64,7 @@ static int print_preallocated(cJSON *root)
/* create buffer to fail */
len_fail = strlen(out);
buf_fail = malloc(len_fail);
buf_fail = (char*)malloc(len_fail);
if (buf_fail == NULL)
{
printf("Failed to allocate memory.\n");
@@ -123,7 +72,7 @@ static int print_preallocated(cJSON *root)
}
/* Print to buffer */
if (!cJSON_PrintPreallocated(root, buf, len, 1)) {
if (!cJSON_PrintPreallocated(root, buf, (int)len, 1)) {
printf("cJSON_PrintPreallocated failed!\n");
if (strcmp(out, buf) != 0) {
printf("cJSON_PrintPreallocated not the same as cJSON_Print!\n");
@@ -140,7 +89,7 @@ static int print_preallocated(cJSON *root)
printf("%s\n", buf);
/* force it to fail */
if (cJSON_PrintPreallocated(root, buf_fail, len_fail, 1)) {
if (cJSON_PrintPreallocated(root, buf_fail, (int)len_fail, 1)) {
printf("cJSON_PrintPreallocated failed to show error with insufficient memory!\n");
printf("cJSON_Print result:\n%s\n", out);
printf("cJSON_PrintPreallocated result:\n%s\n", buf_fail);
@@ -309,94 +258,8 @@ static void create_objects(void)
int main(void)
{
/* a bunch of json: */
char text1[] =
"{\n"
"\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n"
"\"format\": {\"type\": \"rect\", \n"
"\"width\": 1920, \n"
"\"height\": 1080, \n"
"\"interlace\": false,\"frame rate\": 24\n"
"}\n"
"}";
char text2[] = "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";
char text3[] =
"[\n"
" [0, -1, 0],\n"
" [1, 0, 0],\n"
" [0, 0, 1]\n"
"\t]\n";
char text4[] =
"{\n"
"\t\t\"Image\": {\n"
"\t\t\t\"Width\": 800,\n"
"\t\t\t\"Height\": 600,\n"
"\t\t\t\"Title\": \"View from 15th Floor\",\n"
"\t\t\t\"Thumbnail\": {\n"
"\t\t\t\t\"Url\": \"http:/*www.example.com/image/481989943\",\n"
"\t\t\t\t\"Height\": 125,\n"
"\t\t\t\t\"Width\": \"100\"\n"
"\t\t\t},\n"
"\t\t\t\"IDs\": [116, 943, 234, 38793]\n"
"\t\t}\n"
"\t}";
char text5[] =
"[\n"
"\t {\n"
"\t \"precision\": \"zip\",\n"
"\t \"Latitude\": 37.7668,\n"
"\t \"Longitude\": -122.3959,\n"
"\t \"Address\": \"\",\n"
"\t \"City\": \"SAN FRANCISCO\",\n"
"\t \"State\": \"CA\",\n"
"\t \"Zip\": \"94107\",\n"
"\t \"Country\": \"US\"\n"
"\t },\n"
"\t {\n"
"\t \"precision\": \"zip\",\n"
"\t \"Latitude\": 37.371991,\n"
"\t \"Longitude\": -122.026020,\n"
"\t \"Address\": \"\",\n"
"\t \"City\": \"SUNNYVALE\",\n"
"\t \"State\": \"CA\",\n"
"\t \"Zip\": \"94085\",\n"
"\t \"Country\": \"US\"\n"
"\t }\n"
"\t ]";
char text6[] =
"<!DOCTYPE html>"
"<html>\n"
"<head>\n"
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
" <style type=\"text/css\">\n"
" html, body, iframe { margin: 0; padding: 0; height: 100%; }\n"
" iframe { display: block; width: 100%; border: none; }\n"
" </style>\n"
"<title>Application Error</title>\n"
"</head>\n"
"<body>\n"
" <iframe src=\"//s3.amazonaws.com/heroku_pages/error.html\">\n"
" <p>Application Error</p>\n"
" </iframe>\n"
"</body>\n"
"</html>\n";
/* 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();

76
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,76 @@
if(ENABLE_CJSON_TEST)
add_library(unity unity/src/unity.c)
# Disable -Werror for Unity
if (FLAG_SUPPORTED_Werror)
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error")
else()
target_compile_options(unity PRIVATE "-Wno-error")
endif()
endif()
# Disable -fvisibility=hidden for Unity
if (FLAG_SUPPORTED_fvisibilityhidden)
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=default")
else()
target_compile_options(unity PRIVATE "-fvisibility=default")
endif()
endif()
# Disable -fsanitize=float-divide-by-zero for Unity (GCC bug on x86 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80097)
if (FLAG_SUPPORTED_fsanitizefloatdividebyzero AND (CMAKE_C_COMPILER_ID STREQUAL "GNU"))
if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=float-divide-by-zero")
else()
target_compile_options(unity PRIVATE "-fno-sanitize=float-divide-by-zero")
endif()
endif()
#copy test files
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs")
file(GLOB test_files "inputs/*")
file(COPY ${test_files} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/inputs/")
set(unity_tests
parse_examples
parse_number
parse_hex4
parse_string
parse_array
parse_object
parse_value
print_string
print_number
print_array
print_object
print_value
misc_tests
)
add_library(test-common common.c)
option(ENABLE_VALGRIND OFF "Enable the valgrind memory checker for the tests.")
if (ENABLE_VALGRIND)
find_program(MEMORYCHECK_COMMAND valgrind)
if ("${MEMORYCHECK_COMMAND}" MATCHES "MEMORYCHECK_COMMAND-NOTFOUND")
message(WARNING "Valgrind couldn't be found.")
unset(MEMORYCHECK_COMMAND)
else()
set(MEMORYCHECK_COMMAND_OPTIONS --trace-children=yes --leak-check=full --error-exitcode=1)
endif()
endif()
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} "${CMAKE_CURRENT_BINARY_DIR}/${unity_test}")
else()
add_test(NAME "${unity_test}"
COMMAND "./${unity_test}")
endif()
endforeach()
add_dependencies(check ${unity_tests})
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"
CJSON_PUBLIC(void) reset(cJSON *item)
{
if ((item != NULL) && (item->child != NULL))
{
cJSON_Delete(item->child);
}
if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference))
{
global_hooks.deallocate(item->valuestring);
}
if ((item->string != NULL) && !(item->type & cJSON_StringIsConst))
{
global_hooks.deallocate(item->string);
}
memset(item, 0, sizeof(cJSON));
}
CJSON_PUBLIC(char*) read_file(const char *filename)
{
FILE *file = NULL;
long length = 0;
char *content = NULL;
size_t read_chars = 0;
/* open in read binary mode */
file = fopen(filename, "rb");
if (file == NULL)
{
goto cleanup;
}
/* get the length */
if (fseek(file, 0, SEEK_END) != 0)
{
goto cleanup;
}
length = ftell(file);
if (length < 0)
{
goto cleanup;
}
if (fseek(file, 0, SEEK_SET) != 0)
{
goto cleanup;
}
/* allocate content buffer */
content = (char*)malloc((size_t)length + sizeof('\0'));
if (content == NULL)
{
goto cleanup;
}
/* read the file into memory */
read_chars = fread(content, sizeof(char), (size_t)length, file);
if ((long)read_chars != length)
{
free(content);
content = NULL;
goto cleanup;
}
content[read_chars] = '\0';
cleanup:
if (file != NULL)
{
fclose(file);
}
return content;
}

51
tests/common.h Normal file
View File

@@ -0,0 +1,51 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef CJSON_TESTS_COMMON_H
#define CJSON_TESTS_COMMON_H
#include "../cJSON.c"
CJSON_PUBLIC(void) reset(cJSON *item);
CJSON_PUBLIC(char*) read_file(const char *filename);
/* assertion helper macros */
#define assert_has_type(item, item_type) TEST_ASSERT_BITS_MESSAGE(0xFF, item_type, item->type, "Item doesn't have expected type.")
#define assert_has_no_reference(item) TEST_ASSERT_BITS_MESSAGE(cJSON_IsReference, 0, item->type, "Item should not have a string as reference.")
#define assert_has_no_const_string(item) TEST_ASSERT_BITS_MESSAGE(cJSON_StringIsConst, 0, item->type, "Item should not have a const string.")
#define assert_has_valuestring(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->valuestring, "Valuestring is NULL.")
#define assert_has_no_valuestring(item) TEST_ASSERT_NULL_MESSAGE(item->valuestring, "Valuestring is not NULL.")
#define assert_has_string(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->string, "String is NULL")
#define assert_has_no_string(item) TEST_ASSERT_NULL_MESSAGE(item->string, "String is not NULL.")
#define assert_not_in_list(item) \
TEST_ASSERT_NULL_MESSAGE(item->next, "Linked list next pointer is not NULL.");\
TEST_ASSERT_NULL_MESSAGE(item->prev, "Linked list previous pointer is not NULL.")
#define assert_has_child(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->child, "Item doesn't have a child.")
#define assert_has_no_child(item) TEST_ASSERT_NULL_MESSAGE(item->child, "Item has a child.")
#define assert_is_invalid(item) \
assert_has_type(item, cJSON_Invalid);\
assert_not_in_list(item);\
assert_has_no_child(item);\
assert_has_no_string(item);\
assert_has_no_valuestring(item)
#endif

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.7668,
"Longitude": -122.3959,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
"Zip": "94107",
"Country": "US"
}, {
"precision": "zip",
"Latitude": 37.371991,
"Longitude": -122.02602,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
"Zip": "94085",
"Country": "US"
}]

13
tests/inputs/test8 Normal file
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]]

197
tests/misc_tests.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 void cjson_array_foreach_should_loop_over_arrays(void)
{
cJSON array[1];
cJSON elements[10];
cJSON *element_pointer = NULL;
size_t i = 0;
memset(array, 0, sizeof(array));
memset(elements, 0, sizeof(elements));
/* create array */
array[0].child = &elements[0];
elements[0].prev = NULL;
elements[9].next = NULL;
for (i = 0; i < 9; i++)
{
elements[i].next = &elements[i + 1];
elements[i + 1].prev = &elements[i];
}
i = 0;
cJSON_ArrayForEach(element_pointer, array)
{
TEST_ASSERT_TRUE_MESSAGE(element_pointer == &elements[i], "Not iterating over array properly");
i++;
}
}
static void cjson_array_foreach_should_not_dereference_null_pointer(void)
{
cJSON *array = NULL;
cJSON *element = NULL;
cJSON_ArrayForEach(element, array);
}
static void cjson_get_object_item_should_get_object_items(void)
{
cJSON *item = NULL;
cJSON *found = NULL;
item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}");
found = cJSON_GetObjectItem(NULL, "test");
TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer.");
found = cJSON_GetObjectItem(item, NULL);
TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string.");
found = cJSON_GetObjectItem(item, "one");
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1);
found = cJSON_GetObjectItem(item, "tWo");
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2);
found = cJSON_GetObjectItem(item, "three");
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item.");
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3);
found = cJSON_GetObjectItem(item, "four");
TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there.");
cJSON_Delete(item);
}
static void cjson_get_object_item_case_sensitive_should_get_object_items(void)
{
cJSON *item = NULL;
cJSON *found = NULL;
item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}");
found = cJSON_GetObjectItemCaseSensitive(NULL, "test");
TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer.");
found = cJSON_GetObjectItemCaseSensitive(item, NULL);
TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string.");
found = cJSON_GetObjectItemCaseSensitive(item, "one");
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1);
found = cJSON_GetObjectItemCaseSensitive(item, "Two");
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2);
found = cJSON_GetObjectItemCaseSensitive(item, "tHree");
TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item.");
TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3);
found = cJSON_GetObjectItemCaseSensitive(item, "One");
TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there.");
cJSON_Delete(item);
}
static void typecheck_functions_should_check_type(void)
{
cJSON invalid[1];
cJSON item[1];
invalid->type = cJSON_Invalid;
invalid->type |= cJSON_StringIsConst;
item->type = cJSON_False;
item->type |= cJSON_StringIsConst;
TEST_ASSERT_FALSE(cJSON_IsInvalid(NULL));
TEST_ASSERT_FALSE(cJSON_IsInvalid(item));
TEST_ASSERT_TRUE(cJSON_IsInvalid(invalid));
item->type = cJSON_False | cJSON_StringIsConst;
TEST_ASSERT_FALSE(cJSON_IsFalse(NULL));
TEST_ASSERT_FALSE(cJSON_IsFalse(invalid));
TEST_ASSERT_TRUE(cJSON_IsFalse(item));
TEST_ASSERT_TRUE(cJSON_IsBool(item));
item->type = cJSON_True | cJSON_StringIsConst;
TEST_ASSERT_FALSE(cJSON_IsTrue(NULL));
TEST_ASSERT_FALSE(cJSON_IsTrue(invalid));
TEST_ASSERT_TRUE(cJSON_IsTrue(item));
TEST_ASSERT_TRUE(cJSON_IsBool(item));
item->type = cJSON_NULL | cJSON_StringIsConst;
TEST_ASSERT_FALSE(cJSON_IsNull(NULL));
TEST_ASSERT_FALSE(cJSON_IsNull(invalid));
TEST_ASSERT_TRUE(cJSON_IsNull(item));
item->type = cJSON_Number | cJSON_StringIsConst;
TEST_ASSERT_FALSE(cJSON_IsNumber(NULL));
TEST_ASSERT_FALSE(cJSON_IsNumber(invalid));
TEST_ASSERT_TRUE(cJSON_IsNumber(item));
item->type = cJSON_String | cJSON_StringIsConst;
TEST_ASSERT_FALSE(cJSON_IsString(NULL));
TEST_ASSERT_FALSE(cJSON_IsString(invalid));
TEST_ASSERT_TRUE(cJSON_IsString(item));
item->type = cJSON_Array | cJSON_StringIsConst;
TEST_ASSERT_FALSE(cJSON_IsArray(NULL));
TEST_ASSERT_FALSE(cJSON_IsArray(invalid));
TEST_ASSERT_TRUE(cJSON_IsArray(item));
item->type = cJSON_Object | cJSON_StringIsConst;
TEST_ASSERT_FALSE(cJSON_IsObject(NULL));
TEST_ASSERT_FALSE(cJSON_IsObject(invalid));
TEST_ASSERT_TRUE(cJSON_IsObject(item));
item->type = cJSON_Raw | cJSON_StringIsConst;
TEST_ASSERT_FALSE(cJSON_IsRaw(NULL));
TEST_ASSERT_FALSE(cJSON_IsRaw(invalid));
TEST_ASSERT_TRUE(cJSON_IsRaw(item));
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(cjson_array_foreach_should_loop_over_arrays);
RUN_TEST(cjson_array_foreach_should_not_dereference_null_pointer);
RUN_TEST(cjson_get_object_item_should_get_object_items);
RUN_TEST(cjson_get_object_item_case_sensitive_should_get_object_items);
RUN_TEST(typecheck_functions_should_check_type);
return UNITY_END();
}

158
tests/parse_array.c Normal file
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, &global_hooks));
assert_is_invalid(item);
}
static void assert_parse_array(const char *json)
{
TEST_ASSERT_NOT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer, &global_hooks));
assert_is_array(item);
}
static void parse_array_should_parse_empty_arrays(void)
{
assert_parse_array("[]");
assert_has_no_child(item);
assert_parse_array("[\n\t]");
assert_has_no_child(item);
}
static void parse_array_should_parse_arrays_with_one_element(void)
{
assert_parse_array("[1]");
assert_has_child(item);
assert_has_type(item->child, cJSON_Number);
reset(item);
assert_parse_array("[\"hello!\"]");
assert_has_child(item);
assert_has_type(item->child, cJSON_String);
TEST_ASSERT_EQUAL_STRING("hello!", item->child->valuestring);
reset(item);
assert_parse_array("[[]]");
assert_has_child(item);
assert_is_array(item->child);
assert_has_no_child(item->child);
reset(item);
assert_parse_array("[null]");
assert_has_child(item);
assert_has_type(item->child, cJSON_NULL);
reset(item);
}
static void parse_array_should_parse_arrays_with_multiple_elements(void)
{
assert_parse_array("[1\t,\n2, 3]");
assert_has_child(item);
TEST_ASSERT_NOT_NULL(item->child->next);
TEST_ASSERT_NOT_NULL(item->child->next->next);
TEST_ASSERT_NULL(item->child->next->next->next);
assert_has_type(item->child, cJSON_Number);
assert_has_type(item->child->next, cJSON_Number);
assert_has_type(item->child->next->next, cJSON_Number);
reset(item);
{
size_t i = 0;
cJSON *node = NULL;
int expected_types[7] =
{
cJSON_Number,
cJSON_NULL,
cJSON_True,
cJSON_False,
cJSON_Array,
cJSON_String,
cJSON_Object
};
assert_parse_array("[1, null, true, false, [], \"hello\", {}]");
node = item->child;
for (
i = 0;
(i < (sizeof(expected_types)/sizeof(int)))
&& (node != NULL);
(void)i++, node = node->next)
{
TEST_ASSERT_BITS(0xFF, expected_types[i], node->type);
}
TEST_ASSERT_EQUAL_INT(i, 7);
reset(item);
}
}
static void parse_array_should_not_parse_non_arrays(void)
{
assert_not_array("");
assert_not_array("[");
assert_not_array("]");
assert_not_array("{\"hello\":[]}");
assert_not_array("42");
assert_not_array("3.14");
assert_not_array("\"[]hello world!\n\"");
}
int main(void)
{
/* initialize cJSON item */
memset(item, 0, sizeof(cJSON));
UNITY_BEGIN();
RUN_TEST(parse_array_should_parse_empty_arrays);
RUN_TEST(parse_array_should_parse_arrays_with_one_element);
RUN_TEST(parse_array_should_parse_arrays_with_multiple_elements);
RUN_TEST(parse_array_should_not_parse_non_arrays);
return UNITY_END();
}

197
tests/parse_examples.c Normal file
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, &global_hooks));
assert_is_invalid(item);
reset(item);
}
static void assert_parse_object(const char *json)
{
TEST_ASSERT_NOT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer, &global_hooks));
assert_is_object(item);
}
static void parse_object_should_parse_empty_objects(void)
{
assert_parse_object("{}");
assert_has_no_child(item);
reset(item);
assert_parse_object("{\n\t}");
assert_has_no_child(item);
reset(item);
}
static void parse_object_should_parse_objects_with_one_element(void)
{
assert_parse_object("{\"one\":1}");
assert_is_child(item->child, "one", cJSON_Number);
reset(item);
assert_parse_object("{\"hello\":\"world!\"}");
assert_is_child(item->child, "hello", cJSON_String);
reset(item);
assert_parse_object("{\"array\":[]}");
assert_is_child(item->child, "array", cJSON_Array);
reset(item);
assert_parse_object("{\"null\":null}");
assert_is_child(item->child, "null", cJSON_NULL);
reset(item);
}
static void parse_object_should_parse_objects_with_multiple_elements(void)
{
assert_parse_object("{\"one\":1\t,\t\"two\"\n:2, \"three\":3}");
assert_is_child(item->child, "one", cJSON_Number);
assert_is_child(item->child->next, "two", cJSON_Number);
assert_is_child(item->child->next->next, "three", cJSON_Number);
reset(item);
{
size_t i = 0;
cJSON *node = NULL;
int expected_types[7] =
{
cJSON_Number,
cJSON_NULL,
cJSON_True,
cJSON_False,
cJSON_Array,
cJSON_String,
cJSON_Object
};
const char *expected_names[7] =
{
"one",
"NULL",
"TRUE",
"FALSE",
"array",
"world",
"object"
};
assert_parse_object("{\"one\":1, \"NULL\":null, \"TRUE\":true, \"FALSE\":false, \"array\":[], \"world\":\"hello\", \"object\":{}}");
node = item->child;
for (
i = 0;
(i < (sizeof(expected_types)/sizeof(int)))
&& (node != NULL);
(void)i++, node = node->next)
{
assert_is_child(node, expected_names[i], expected_types[i]);
}
TEST_ASSERT_EQUAL_INT(i, 7);
reset(item);
}
}
static void parse_object_should_not_parse_non_objects(void)
{
assert_not_object("");
assert_not_object("{");
assert_not_object("}");
assert_not_object("[\"hello\",{}]");
assert_not_object("42");
assert_not_object("3.14");
assert_not_object("\"{}hello world!\n\"");
}
int main(void)
{
/* initialize cJSON item */
memset(item, 0, sizeof(cJSON));
UNITY_BEGIN();
RUN_TEST(parse_object_should_parse_empty_objects);
RUN_TEST(parse_object_should_not_parse_non_objects);
RUN_TEST(parse_object_should_parse_objects_with_multiple_elements);
RUN_TEST(parse_object_should_parse_objects_with_one_element);
return UNITY_END();
}

125
tests/parse_string.c Normal file
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, &global_hooks), "Couldn't parse string.");
assert_is_string(item);
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, item->valuestring, "The parsed result isn't as expected.");
global_hooks.deallocate(item->valuestring);
item->valuestring = NULL;
}
#define assert_not_parse_string(string) \
TEST_ASSERT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer, &global_hooks), "Malformed string should not be accepted");\
assert_is_invalid(item)
static void parse_string_should_parse_strings(void)
{
assert_parse_string("\"\"", "");
assert_parse_string(
"\" !\\\"#$%&'()*+,-./\\/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_'abcdefghijklmnopqrstuvwxyz{|}~\"",
" !\"#$%&'()*+,-.//0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'abcdefghijklmnopqrstuvwxyz{|}~");
assert_parse_string(
"\"\\\"\\\\\\/\\b\\f\\n\\r\\t\\u20AC\\u732b\"",
"\"\\/\b\f\n\r\t€猫");
reset(item);
assert_parse_string("\"\b\f\n\r\t\"", "\b\f\n\r\t");
reset(item);
}
static void parse_string_should_parse_utf16_surrogate_pairs(void)
{
assert_parse_string("\"\\uD83D\\udc31\"", "🐱");
reset(item);
}
static void parse_string_should_not_parse_non_strings(void)
{
assert_not_parse_string("this\" is not a string\"");
reset(item);
assert_not_parse_string("");
reset(item);
}
static void parse_string_should_not_parse_invalid_backslash(void)
{
assert_not_parse_string("Abcdef\\123");
reset(item);
assert_not_parse_string("Abcdef\\e23");
reset(item);
}
static void parse_string_should_not_overflow_with_closing_backslash(void)
{
assert_not_parse_string("\"000000000000000000\\");
reset(item);
}
static void parse_string_should_parse_bug_94(void)
{
const char string[] = "\"~!@\\\\#$%^&*()\\\\\\\\-\\\\+{}[]:\\\\;\\\\\\\"\\\\<\\\\>?/.,DC=ad,DC=com\"";
assert_parse_string(string, "~!@\\#$%^&*()\\\\-\\+{}[]:\\;\\\"\\<\\>?/.,DC=ad,DC=com");
reset(item);
}
int main(void)
{
/* initialize cJSON item and error pointer */
memset(item, 0, sizeof(cJSON));
UNITY_BEGIN();
RUN_TEST(parse_string_should_parse_strings);
RUN_TEST(parse_string_should_parse_utf16_surrogate_pairs);
RUN_TEST(parse_string_should_not_parse_non_strings);
RUN_TEST(parse_string_should_not_parse_invalid_backslash);
RUN_TEST(parse_string_should_parse_bug_94);
RUN_TEST(parse_string_should_not_overflow_with_closing_backslash);
return UNITY_END();
}

108
tests/parse_value.c Normal file
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, &global_hooks));
assert_is_value(item, type);
}
static void parse_value_should_parse_null(void)
{
assert_parse_value("null", cJSON_NULL);
reset(item);
}
static void parse_value_should_parse_true(void)
{
assert_parse_value("true", cJSON_True);
reset(item);
}
static void parse_value_should_parse_false(void)
{
assert_parse_value("false", cJSON_False);
reset(item);
}
static void parse_value_should_parse_number(void)
{
assert_parse_value("1.5", cJSON_Number);
reset(item);
}
static void parse_value_should_parse_string(void)
{
assert_parse_value("\"\"", cJSON_String);
reset(item);
assert_parse_value("\"hello\"", cJSON_String);
reset(item);
}
static void parse_value_should_parse_array(void)
{
assert_parse_value("[]", cJSON_Array);
reset(item);
}
static void parse_value_should_parse_object(void)
{
assert_parse_value("{}", cJSON_Object);
reset(item);
}
int main(void)
{
/* initialize cJSON item */
memset(item, 0, sizeof(cJSON));
UNITY_BEGIN();
RUN_TEST(parse_value_should_parse_null);
RUN_TEST(parse_value_should_parse_true);
RUN_TEST(parse_value_should_parse_false);
RUN_TEST(parse_value_should_parse_number);
RUN_TEST(parse_value_should_parse_string);
RUN_TEST(parse_value_should_parse_array);
RUN_TEST(parse_value_should_parse_object);
return UNITY_END();
}

92
tests/print_array.c Normal file
View File

@@ -0,0 +1,92 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
static void assert_print_array(const char * const expected, const char * const input)
{
unsigned char printed_unformatted[1024];
unsigned char printed_formatted[1024];
const unsigned char *error_pointer;
cJSON item[1];
printbuffer formatted_buffer;
printbuffer unformatted_buffer;
/* buffer for formatted printing */
formatted_buffer.buffer = printed_formatted;
formatted_buffer.length = sizeof(printed_formatted);
formatted_buffer.offset = 0;
formatted_buffer.noalloc = true;
/* buffer for unformatted printing */
unformatted_buffer.buffer = printed_unformatted;
unformatted_buffer.length = sizeof(printed_unformatted);
unformatted_buffer.offset = 0;
unformatted_buffer.noalloc = true;
memset(item, 0, sizeof(item));
TEST_ASSERT_NOT_NULL_MESSAGE(parse_array(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse array.");
TEST_ASSERT_TRUE_MESSAGE(print_array(item, 0, false, &unformatted_buffer, &global_hooks), "Failed to print unformatted string.");
TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted array is not correct.");
TEST_ASSERT_TRUE_MESSAGE(print_array(item, 0, true, &formatted_buffer, &global_hooks), "Failed to print formatted string.");
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted array is not correct.");
reset(item);
}
static void print_array_should_print_empty_arrays(void)
{
assert_print_array("[]", "[]");
}
static void print_array_should_print_arrays_with_one_element(void)
{
assert_print_array("[1]", "[1]");
assert_print_array("[\"hello!\"]", "[\"hello!\"]");
assert_print_array("[[]]", "[[]]");
assert_print_array("[null]", "[null]");
}
static void print_array_should_print_arrays_with_multiple_elements(void)
{
assert_print_array("[1, 2, 3]", "[1,2,3]");
assert_print_array("[1, null, true, false, [], \"hello\", {\n\t}]", "[1,null,true,false,[],\"hello\",{}]");
}
int main(void)
{
/* initialize cJSON item */
UNITY_BEGIN();
RUN_TEST(print_array_should_print_empty_arrays);
RUN_TEST(print_array_should_print_arrays_with_one_element);
RUN_TEST(print_array_should_print_arrays_with_multiple_elements);
return UNITY_END();
}

119
tests/print_number.c Normal file
View File

@@ -0,0 +1,119 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
static void assert_print_number(const char *expected, double input)
{
unsigned char printed[1024];
cJSON item[1];
printbuffer buffer;
buffer.buffer = printed;
buffer.length = sizeof(printed);
buffer.offset = 0;
buffer.noalloc = true;
memset(item, 0, sizeof(item));
cJSON_SetNumberValue(item, input);
TEST_ASSERT_TRUE_MESSAGE(print_number(item, &buffer, &global_hooks), "Failed to print number.");
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buffer.buffer, "Printed number is not as expected.");
}
static void print_number_should_print_zero(void)
{
assert_print_number("0", 0);
}
static void print_number_should_print_negative_integers(void)
{
assert_print_number("-1", -1);
assert_print_number("-32768", -32768);
assert_print_number("-2147483648", -2147483648.0);
}
static void print_number_should_print_positive_integers(void)
{
assert_print_number("1", 1);
assert_print_number("32767", 32767);
assert_print_number("2147483647", 2147483647);
}
static void print_number_should_print_positive_reals(void)
{
assert_print_number("0.123", 0.123);
assert_print_number("1.000000e-09", 10e-10);
assert_print_number("1000000000000", 10e11);
assert_print_number("1.230000e+129", 123e+127);
assert_print_number("0", 123e-128); /* TODO: Maybe this shouldn't be 0 */
}
static void print_number_should_print_negative_reals(void)
{
assert_print_number("-0.0123", -0.0123);
assert_print_number("-1.000000e-09", -10e-10);
assert_print_number("-1000000000000000000000", -10e20);
assert_print_number("-1.230000e+129", -123e+127);
assert_print_number("-1.230000e-126", -123e-128);
}
static void print_number_should_print_non_number(void)
{
TEST_IGNORE();
/* FIXME: Cannot test this easily in C89! */
/* assert_print_number("null", NaN); */
/* assert_print_number("null", INFTY); */
/* assert_print_number("null", -INFTY); */
}
static void trim_trailing_zeroes_should_trim_trailing_zeroes(void)
{
printbuffer buffer;
unsigned char number[100];
buffer.length = sizeof(number);
buffer.buffer = number;
strcpy((char*)number, "10.00");
buffer.offset = sizeof("10.00") - 1;
TEST_ASSERT_TRUE(trim_trailing_zeroes(&buffer));
TEST_ASSERT_EQUAL_UINT8('\0', buffer.buffer[buffer.offset]);
TEST_ASSERT_EQUAL_STRING("10", number);
TEST_ASSERT_EQUAL_UINT(sizeof("10") - 1, buffer.offset);
}
int main(void)
{
/* initialize cJSON item */
UNITY_BEGIN();
RUN_TEST(print_number_should_print_zero);
RUN_TEST(print_number_should_print_negative_integers);
RUN_TEST(print_number_should_print_positive_integers);
RUN_TEST(print_number_should_print_positive_reals);
RUN_TEST(print_number_should_print_negative_reals);
RUN_TEST(print_number_should_print_non_number);
RUN_TEST(trim_trailing_zeroes_should_trim_trailing_zeroes);
return UNITY_END();
}

92
tests/print_object.c Normal file
View File

@@ -0,0 +1,92 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
static void assert_print_object(const char * const expected, const char * const input)
{
unsigned char printed_unformatted[1024];
unsigned char printed_formatted[1024];
const unsigned char *error_pointer;
cJSON item[1];
printbuffer formatted_buffer;
printbuffer unformatted_buffer;
/* buffer for formatted printing */
formatted_buffer.buffer = printed_formatted;
formatted_buffer.length = sizeof(printed_formatted);
formatted_buffer.offset = 0;
formatted_buffer.noalloc = true;
/* buffer for unformatted printing */
unformatted_buffer.buffer = printed_unformatted;
unformatted_buffer.length = sizeof(printed_unformatted);
unformatted_buffer.offset = 0;
unformatted_buffer.noalloc = true;
memset(item, 0, sizeof(item));
TEST_ASSERT_NOT_NULL_MESSAGE(parse_object(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse object.");
TEST_ASSERT_TRUE_MESSAGE(print_object(item, 0, false, &unformatted_buffer, &global_hooks), "Failed to print unformatted string.");
TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted object is not correct.");
TEST_ASSERT_TRUE_MESSAGE(print_object(item, 0, true, &formatted_buffer, &global_hooks), "Failed to print formatted string.");
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted ojbect is not correct.");
reset(item);
}
static void print_object_should_print_empty_objects(void)
{
assert_print_object("{\n}", "{}");
}
static void print_object_should_print_objects_with_one_element(void)
{
assert_print_object("{\n\t\"one\":\t1\n}", "{\"one\":1}");
assert_print_object("{\n\t\"hello\":\t\"world!\"\n}", "{\"hello\":\"world!\"}");
assert_print_object("{\n\t\"array\":\t[]\n}", "{\"array\":[]}");
assert_print_object("{\n\t\"null\":\tnull\n}", "{\"null\":null}");
}
static void print_object_should_print_objects_with_multiple_elements(void)
{
assert_print_object("{\n\t\"one\":\t1,\n\t\"two\":\t2,\n\t\"three\":\t3\n}", "{\"one\":1,\"two\":2,\"three\":3}");
assert_print_object("{\n\t\"one\":\t1,\n\t\"NULL\":\tnull,\n\t\"TRUE\":\ttrue,\n\t\"FALSE\":\tfalse,\n\t\"array\":\t[],\n\t\"world\":\t\"hello\",\n\t\"object\":\t{\n\t}\n}", "{\"one\":1,\"NULL\":null,\"TRUE\":true,\"FALSE\":false,\"array\":[],\"world\":\"hello\",\"object\":{}}");
}
int main(void)
{
/* initialize cJSON item */
UNITY_BEGIN();
RUN_TEST(print_object_should_print_empty_objects);
RUN_TEST(print_object_should_print_objects_with_one_element);
RUN_TEST(print_object_should_print_objects_with_multiple_elements);
return UNITY_END();
}

77
tests/print_string.c Normal file
View File

@@ -0,0 +1,77 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
static void assert_print_string(const char *expected, const char *input)
{
unsigned char printed[1024];
printbuffer buffer;
buffer.buffer = printed;
buffer.length = sizeof(printed);
buffer.offset = 0;
buffer.noalloc = true;
TEST_ASSERT_TRUE_MESSAGE(print_string_ptr((const unsigned char*)input, &buffer, &global_hooks), "Failed to print string.");
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed, "The printed string isn't as expected.");
}
static void print_string_should_print_empty_strings(void)
{
assert_print_string("\"\"", "");
assert_print_string("\"\"", NULL);
}
static void print_string_should_print_ascii(void)
{
char ascii[0x7F];
size_t i = 1;
/* create ascii table */
for (i = 1; i < 0x7F; i++)
{
ascii[i-1] = (char)i;
}
ascii[0x7F-1] = '\0';
assert_print_string("\"\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"",
ascii);
}
static void print_string_should_print_utf8(void)
{
assert_print_string("\"ü猫慕\"", "ü猫慕");
}
int main(void)
{
/* initialize cJSON item */
UNITY_BEGIN();
RUN_TEST(print_string_should_print_empty_strings);
RUN_TEST(print_string_should_print_ascii);
RUN_TEST(print_string_should_print_utf8);
return UNITY_END();
}

102
tests/print_value.c Normal file
View File

@@ -0,0 +1,102 @@
/*
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "unity/examples/unity_config.h"
#include "unity/src/unity.h"
#include "common.h"
static void assert_print_value(const char *input)
{
unsigned char printed[1024];
const unsigned char *error_pointer = NULL;
cJSON item[1];
printbuffer buffer;
buffer.buffer = printed;
buffer.length = sizeof(printed);
buffer.offset = 0;
buffer.noalloc = true;
memset(item, 0, sizeof(item));
TEST_ASSERT_NOT_NULL_MESSAGE(parse_value(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse value.");
TEST_ASSERT_TRUE_MESSAGE(print_value(item, 0, false, &buffer, &global_hooks), "Failed to print value.");
TEST_ASSERT_EQUAL_STRING_MESSAGE(input, buffer.buffer, "Printed value is not as expected.");
reset(item);
}
static void print_value_should_print_null(void)
{
assert_print_value("null");
}
static void print_value_should_print_true(void)
{
assert_print_value("true");
}
static void print_value_should_print_false(void)
{
assert_print_value("false");
}
static void print_value_should_print_number(void)
{
assert_print_value("1.5");
}
static void print_value_should_print_string(void)
{
assert_print_value("\"\"");
assert_print_value("\"hello\"");
}
static void print_value_should_print_array(void)
{
assert_print_value("[]");
}
static void print_value_should_print_object(void)
{
assert_print_value("{}");
}
int main(void)
{
/* initialize cJSON item */
UNITY_BEGIN();
RUN_TEST(print_value_should_print_null);
RUN_TEST(print_value_should_print_true);
RUN_TEST(print_value_should_print_false);
RUN_TEST(print_value_should_print_number);
RUN_TEST(print_value_should_print_string);
RUN_TEST(print_value_should_print_array);
RUN_TEST(print_value_should_print_object);
return UNITY_END();
}

30
tests/unity/.gitattributes vendored Normal file
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())
{
MyTest();
}
}
If MyTest calls `TEST_ABORT`, program control will immediately return to `TEST_PROTECT` with a return value of zero.
Unity Assertion Summary
=======================
Basic Validity Tests
--------------------
TEST_ASSERT_TRUE(condition)
Evaluates whatever code is in condition and fails if it evaluates to false
TEST_ASSERT_FALSE(condition)
Evaluates whatever code is in condition and fails if it evaluates to true
TEST_ASSERT(condition)
Another way of calling `TEST_ASSERT_TRUE`
TEST_ASSERT_UNLESS(condition)
Another way of calling `TEST_ASSERT_FALSE`
TEST_FAIL()
TEST_FAIL_MESSAGE(message)
This test is automatically marked as a failure. The message is output stating why.
Numerical Assertions: Integers
------------------------------
TEST_ASSERT_EQUAL_INT(expected, actual)
TEST_ASSERT_EQUAL_INT8(expected, actual)
TEST_ASSERT_EQUAL_INT16(expected, actual)
TEST_ASSERT_EQUAL_INT32(expected, actual)
TEST_ASSERT_EQUAL_INT64(expected, actual)
Compare two integers for equality and display errors as signed integers. A cast will be performed
to your natural integer size so often this can just be used. When you need to specify the exact size,
like when comparing arrays, you can use a specific version:
TEST_ASSERT_EQUAL_UINT(expected, actual)
TEST_ASSERT_EQUAL_UINT8(expected, actual)
TEST_ASSERT_EQUAL_UINT16(expected, actual)
TEST_ASSERT_EQUAL_UINT32(expected, actual)
TEST_ASSERT_EQUAL_UINT64(expected, actual)
Compare two integers for equality and display errors as unsigned integers. Like INT, there are
variants for different sizes also.
TEST_ASSERT_EQUAL_HEX(expected, actual)
TEST_ASSERT_EQUAL_HEX8(expected, actual)
TEST_ASSERT_EQUAL_HEX16(expected, actual)
TEST_ASSERT_EQUAL_HEX32(expected, actual)
TEST_ASSERT_EQUAL_HEX64(expected, actual)
Compares two integers for equality and display errors as hexadecimal. Like the other integer comparisons,
you can specify the size... here the size will also effect how many nibbles are shown (for example, `HEX16`
will show 4 nibbles).
_ARRAY
You can append `_ARRAY` to any of these macros to make an array comparison of that type. Here you will
need to care a bit more about the actual size of the value being checked. You will also specify an
additional argument which is the number of elements to compare. For example:
TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, actual, elements)
TEST_ASSERT_EQUAL(expected, actual)
Another way of calling TEST_ASSERT_EQUAL_INT
TEST_ASSERT_INT_WITHIN(delta, expected, actual)
Asserts that the actual value is within plus or minus delta of the expected value. This also comes in
size specific variants.
Numerical Assertions: Bitwise
-----------------------------
TEST_ASSERT_BITS(mask, expected, actual)
Use an integer mask to specify which bits should be compared between two other integers. High bits in the mask are compared, low bits ignored.
TEST_ASSERT_BITS_HIGH(mask, actual)
Use an integer mask to specify which bits should be inspected to determine if they are all set high. High bits in the mask are compared, low bits ignored.
TEST_ASSERT_BITS_LOW(mask, actual)
Use an integer mask to specify which bits should be inspected to determine if they are all set low. High bits in the mask are compared, low bits ignored.
TEST_ASSERT_BIT_HIGH(bit, actual)
Test a single bit and verify that it is high. The bit is specified 0-31 for a 32-bit integer.
TEST_ASSERT_BIT_LOW(bit, actual)
Test a single bit and verify that it is low. The bit is specified 0-31 for a 32-bit integer.
Numerical Assertions: Floats
----------------------------
TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual)
Asserts that the actual value is within plus or minus delta of the expected value.
TEST_ASSERT_EQUAL_FLOAT(expected, actual)
TEST_ASSERT_EQUAL_DOUBLE(expected, actual)
Asserts that two floating point values are "equal" within a small % delta of the expected value.
String Assertions
-----------------
TEST_ASSERT_EQUAL_STRING(expected, actual)
Compare two null-terminate strings. Fail if any character is different or if the lengths are different.
TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len)
Compare two strings. Fail if any character is different, stop comparing after len characters.
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, actual, message)
Compare two null-terminate strings. Fail if any character is different or if the lengths are different. Output a custom message on failure.
TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(expected, actual, len, message)
Compare two strings. Fail if any character is different, stop comparing after len characters. Output a custom message on failure.
Pointer Assertions
------------------
Most pointer operations can be performed by simply using the integer comparisons above. However, a couple of special cases are added for clarity.
TEST_ASSERT_NULL(pointer)
Fails if the pointer is not equal to NULL
TEST_ASSERT_NOT_NULL(pointer)
Fails if the pointer is equal to NULL
Memory Assertions
-----------------
TEST_ASSERT_EQUAL_MEMORY(expected, actual, len)
Compare two blocks of memory. This is a good generic assertion for types that can't be coerced into acting like
standard types... but since it's a memory compare, you have to be careful that your data types are packed.
_MESSAGE
--------
you can append _MESSAGE to any of the macros to make them take an additional argument. This argument
is a string that will be printed at the end of the failure strings. This is useful for specifying more
information about the problem.

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,224 @@
#============================================================
# Author: John Theofanopoulos
# A simple parser. Takes the output files generated during the build process and
# extracts information relating to the tests.
#
# Notes:
# To capture an output file under VS builds use the following:
# devenv [build instructions] > Output.txt & type Output.txt
#
# To capture an output file under GCC/Linux builds use the following:
# make | tee Output.txt
#
# To use this parser use the following command
# ruby parseOutput.rb [options] [file]
# options: -xml : produce a JUnit compatible XML file
# file : file to scan for results
#============================================================
class ParseOutput
# The following flag is set to true when a test is found or false otherwise.
@testFlag
@xmlOut
@arrayList
@totalTests
@classIndex
# Set the flag to indicate if there will be an XML output file or not
def setXmlOutput()
@xmlOut = true
end
# if write our output to XML
def writeXmlOuput()
output = File.open("report.xml", "w")
output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
@arrayList.each do |item|
output << item << "\n"
end
output << "</testsuite>\n"
end
# This function will try and determine when the suite is changed. This is
# is the name that gets added to the classname parameter.
def testSuiteVerify(testSuiteName)
if @testFlag == false
@testFlag = true;
# Split the path name
testName = testSuiteName.split("/")
# Remove the extension
baseName = testName[testName.size - 1].split(".")
@testSuite = "test." + baseName[0]
printf "New Test: %s\n", @testSuite
end
end
# Test was flagged as having passed so format the output
def testPassed(array)
lastItem = array.length - 1
testName = array[lastItem - 1]
testSuiteVerify(array[@className])
printf "%-40s PASS\n", testName
if @xmlOut == true
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\"/>"
end
end
# Test was flagged as having passed so format the output.
# This is using the Unity fixture output and not the original Unity output.
def testPassedUnityFixture(array)
testSuite = array[0].sub("TEST(", "")
testSuite = testSuite.sub(",", "")
testName = array[1].sub(")", "")
if @xmlOut == true
@arrayList.push " <testcase classname=\"" + testSuite + "\" name=\"" + testName + "\"/>"
end
end
# Test was flagged as being ingored so format the output
def testIgnored(array)
lastItem = array.length - 1
testName = array[lastItem - 2]
reason = array[lastItem].chomp
testSuiteVerify(array[@className])
printf "%-40s IGNORED\n", testName
if testName.start_with? "TEST("
array2 = testName.split(" ")
@testSuite = array2[0].sub("TEST(", "")
@testSuite = @testSuite.sub(",", "")
testName = array2[1].sub(")", "")
end
if @xmlOut == true
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
@arrayList.push " <skipped type=\"TEST IGNORED\"> " + reason + " </skipped>"
@arrayList.push " </testcase>"
end
end
# Test was flagged as having failed so format the line
def testFailed(array)
lastItem = array.length - 1
testName = array[lastItem - 2]
reason = array[lastItem].chomp + " at line: " + array[lastItem - 3]
testSuiteVerify(array[@className])
printf "%-40s FAILED\n", testName
if testName.start_with? "TEST("
array2 = testName.split(" ")
@testSuite = array2[0].sub("TEST(", "")
@testSuite = @testSuite.sub(",", "")
testName = array2[1].sub(")", "")
end
if @xmlOut == true
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
@arrayList.push " <failure type=\"ASSERT FAILED\"> " + reason + " </failure>"
@arrayList.push " </testcase>"
end
end
# Figure out what OS we are running on. For now we are assuming if it's not Windows it must
# be Unix based.
def detectOS()
myOS = RUBY_PLATFORM.split("-")
if myOS.size == 2
if myOS[1] == "mingw32"
@className = 1
else
@className = 0
end
else
@className = 0
end
end
# Main function used to parse the file that was captured.
def process(name)
@testFlag = false
@arrayList = Array.new
detectOS()
puts "Parsing file: " + name
testPass = 0
testFail = 0
testIgnore = 0
puts ""
puts "=================== RESULTS ====================="
puts ""
File.open(name).each do |line|
# Typical test lines look like this:
# <path>/<test_file>.c:36:test_tc1000_opsys:FAIL: Expected 1 Was 0
# <path>/<test_file>.c:112:test_tc5004_initCanChannel:IGNORE: Not Yet Implemented
# <path>/<test_file>.c:115:test_tc5100_initCanVoidPtrs:PASS
#
# where path is different on Unix vs Windows devices (Windows leads with a drive letter)
lineArray = line.split(":")
lineSize = lineArray.size
# If we were able to split the line then we can look to see if any of our target words
# were found. Case is important.
if ((lineSize >= 4) || (line.start_with? "TEST("))
# Determine if this test passed
if line.include? ":PASS"
testPassed(lineArray)
testPass += 1
elsif line.include? ":FAIL:"
testFailed(lineArray)
testFail += 1
elsif line.include? ":IGNORE:"
testIgnored(lineArray)
testIgnore += 1
elsif line.start_with? "TEST("
if line.include? " PASS"
lineArray = line.split(" ")
testPassedUnityFixture(lineArray)
testPass += 1
end
# If none of the keywords are found there are no more tests for this suite so clear
# the test flag
else
@testFlag = false
end
else
@testFlag = false
end
end
puts ""
puts "=================== SUMMARY ====================="
puts ""
puts "Tests Passed : " + testPass.to_s
puts "Tests Failed : " + testFail.to_s
puts "Tests Ignored : " + testIgnore.to_s
@totalTests = testPass + testFail + testIgnore
if @xmlOut == true
heading = "<testsuite tests=\"" + @totalTests.to_s + "\" failures=\"" + testFail.to_s + "\"" + " skips=\"" + testIgnore.to_s + "\">"
@arrayList.insert(0, heading)
writeXmlOuput()
end
# return result
end
end
# If the command line has no values in, used a default value of Output.txt
parseMyFile = ParseOutput.new
if ARGV.size >= 1
ARGV.each do |a|
if a == "-xml"
parseMyFile.setXmlOutput();
else
parseMyFile.process(a)
break
end
end
end

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

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