Compare commits
386 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c9f76c100 | ||
|
|
2c9947eec9 | ||
|
|
9a85c26161 | ||
|
|
c0088e1ebe | ||
|
|
8738160e16 | ||
|
|
eb6dd6ef6b | ||
|
|
98fb2c9437 | ||
|
|
1217ca9e9f | ||
|
|
6e9c6edd91 | ||
|
|
2d6a2e0133 | ||
|
|
d9b23039b8 | ||
|
|
a01267c0a4 | ||
|
|
ffb877c94a | ||
|
|
e1f6470dae | ||
|
|
1d277eb99d | ||
|
|
9857ab9835 | ||
|
|
66f75619d9 | ||
|
|
7f22948eec | ||
|
|
b674519695 | ||
|
|
94057fb069 | ||
|
|
3bf2913634 | ||
|
|
cd4d7b9b6e | ||
|
|
73823c4b6d | ||
|
|
0aaef1a8fa | ||
|
|
e88be8681d | ||
|
|
222686513e | ||
|
|
62b08f1336 | ||
|
|
7de847eeea | ||
|
|
af9c76e37e | ||
|
|
2f33e8ec9b | ||
|
|
30906a01c0 | ||
|
|
eb017abe72 | ||
|
|
487a8b76a1 | ||
|
|
1d85450883 | ||
|
|
2616db9e92 | ||
|
|
23cbb173d4 | ||
|
|
a29c468b68 | ||
|
|
1d65f33d3d | ||
|
|
997ca129a2 | ||
|
|
cf1770b0ea | ||
|
|
aefe2f85ab | ||
|
|
7f645de322 | ||
|
|
e6c239db2b | ||
|
|
f4d9e2e1a1 | ||
|
|
e3ed64d56e | ||
|
|
6b01dee7ca | ||
|
|
b73881a388 | ||
|
|
bb2f868126 | ||
|
|
11b8a8cd76 | ||
|
|
01d656bebc | ||
|
|
7bf62ff4dc | ||
|
|
f030058183 | ||
|
|
a67c24c451 | ||
|
|
512c313111 | ||
|
|
e591d4173a | ||
|
|
bde341edd8 | ||
|
|
48c97985d6 | ||
|
|
63db67bfeb | ||
|
|
2040ce9004 | ||
|
|
4ba6bafe34 | ||
|
|
378a333ee2 | ||
|
|
4642d6575f | ||
|
|
4932c80f26 | ||
|
|
674a678819 | ||
|
|
b8e3673d0f | ||
|
|
b16bcc3c19 | ||
|
|
44d313212b | ||
|
|
055c7007e5 | ||
|
|
a1a860cd31 | ||
|
|
bc622fcc15 | ||
|
|
dede4e3246 | ||
|
|
8b953d1202 | ||
|
|
2d07bbc9b2 | ||
|
|
8816a568ab | ||
|
|
33193ea557 | ||
|
|
acb0ca88df | ||
|
|
c24c3e0285 | ||
|
|
cc3a75dd39 | ||
|
|
204c7ee43f | ||
|
|
48b3909946 | ||
|
|
b00ecfd311 | ||
|
|
87fad25c2b | ||
|
|
a69ad22639 | ||
|
|
28570ad095 | ||
|
|
303660ae04 | ||
|
|
18a7828940 | ||
|
|
7fe8d53c19 | ||
|
|
ee3c3bc80e | ||
|
|
18b4e2f941 | ||
|
|
ab77a80e9b | ||
|
|
949c083315 | ||
|
|
c4c217f2ce | ||
|
|
79f30800ec | ||
|
|
e0d3a8a265 | ||
|
|
5aa152fa83 | ||
|
|
a400888c1f | ||
|
|
3fc9bc0648 | ||
|
|
05f75e360b | ||
|
|
32626cc251 | ||
|
|
24cf9308de | ||
|
|
51d3df6c9f | ||
|
|
6ccfd5d603 | ||
|
|
49ebc85e7a | ||
|
|
8fd46d51b1 | ||
|
|
609bb75421 | ||
|
|
cea3fe4165 | ||
|
|
b759ff38b8 | ||
|
|
ba7b48b3f3 | ||
|
|
134ebf5e89 | ||
|
|
02a05eea4e | ||
|
|
d67b008d4b | ||
|
|
c66342d871 | ||
|
|
c960b2b853 | ||
|
|
3056d85f01 | ||
|
|
b470d918f3 | ||
|
|
8efb287ae2 | ||
|
|
a1602f484b | ||
|
|
d058a9cd8f | ||
|
|
f11b5eadc8 | ||
|
|
075a06f40b | ||
|
|
62ba68fc7d | ||
|
|
7276f4df05 | ||
|
|
8c900b9e07 | ||
|
|
e0a3c2370d | ||
|
|
3c18c83513 | ||
|
|
6ac896d8d2 | ||
|
|
2a25abbf2a | ||
|
|
e9803341d5 | ||
|
|
31400affab | ||
|
|
6c3df7eb59 | ||
|
|
a99d254e4f | ||
|
|
f0230a584f | ||
|
|
2148e3277a | ||
|
|
c786cd9714 | ||
|
|
238ceec175 | ||
|
|
6702037b68 | ||
|
|
eb5000ba61 | ||
|
|
655c26ada1 | ||
|
|
218b0c8dcf | ||
|
|
281df6997c | ||
|
|
bc0bb7c224 | ||
|
|
56a32f9ecc | ||
|
|
770e8263a5 | ||
|
|
2683589d93 | ||
|
|
c837177a56 | ||
|
|
80bc7652ae | ||
|
|
87a204ed0b | ||
|
|
c9739c59fd | ||
|
|
778a0c146f | ||
|
|
f69b109c9f | ||
|
|
fef1c45b92 | ||
|
|
033a542532 | ||
|
|
fd0320cf54 | ||
|
|
0f98214e71 | ||
|
|
06a2326e3b | ||
|
|
11131b9ced | ||
|
|
99db5db9a4 | ||
|
|
bb5ab5916f | ||
|
|
411c50f671 | ||
|
|
ec2db50b6f | ||
|
|
74d0525201 | ||
|
|
3efef58c32 | ||
|
|
65541b900c | ||
|
|
c08f7e1d29 | ||
|
|
71b96afc27 | ||
|
|
749fefc0c4 | ||
|
|
50b3c30dfa | ||
|
|
c784f76c77 | ||
|
|
84237ff48e | ||
|
|
ab8489a851 | ||
|
|
795c3acabe | ||
|
|
2683d4d987 | ||
|
|
3c1bfe125c | ||
|
|
5916f70640 | ||
|
|
29cfc7a767 | ||
|
|
b1e9a6c0da | ||
|
|
3a20692c18 | ||
|
|
2f65e80a34 | ||
|
|
ef34500693 | ||
|
|
b0dfcde04c | ||
|
|
1934059554 | ||
|
|
cc84a446be | ||
|
|
e58f7ec027 | ||
|
|
4bfb880093 | ||
|
|
b7ce06224b | ||
|
|
227d3398d6 | ||
|
|
466eb8e3f8 | ||
|
|
4ec6e76ea2 | ||
|
|
a1b37d0abe | ||
|
|
3d971db426 | ||
|
|
30e1e7af7c | ||
|
|
76e5296d0d | ||
|
|
c597601cf1 | ||
|
|
e3d5798896 | ||
|
|
cf1842dc6f | ||
|
|
030d0c14cc | ||
|
|
02cd3eec37 | ||
|
|
bdb59f09de | ||
|
|
5f783fff11 | ||
|
|
7969af6434 | ||
|
|
0bb1843925 | ||
|
|
5ea4fad263 | ||
|
|
1749de02f8 | ||
|
|
748f4bfd4f | ||
|
|
bea1d102fd | ||
|
|
d441fa05b3 | ||
|
|
3fe72cf2b8 | ||
|
|
d8d0ae66d3 | ||
|
|
9d07917feb | ||
|
|
2c45ad7816 | ||
|
|
ad5abf4c5b | ||
|
|
6405fd15e3 | ||
|
|
b44c917be9 | ||
|
|
2d3520e0b9 | ||
|
|
2e118df0cd | ||
|
|
412f4f7d62 | ||
|
|
0aea75fbda | ||
|
|
0c0dd4a5b0 | ||
|
|
dd4cb5400f | ||
|
|
1ea72f8260 | ||
|
|
e78bc42362 | ||
|
|
6f271e511f | ||
|
|
bee069b4e7 | ||
|
|
1e0bd24f2c | ||
|
|
5cf56fa4fa | ||
|
|
3f349a4258 | ||
|
|
40e3781e9b | ||
|
|
b056d7cb74 | ||
|
|
1f422b586a | ||
|
|
899529e866 | ||
|
|
2837aac23e | ||
|
|
c66d95d116 | ||
|
|
cb6df3ffad | ||
|
|
695d8a01a9 | ||
|
|
024f690289 | ||
|
|
039b1cc653 | ||
|
|
c0ff1fef9b | ||
|
|
2302f4f0b2 | ||
|
|
c45dc12fd7 | ||
|
|
ed8dc53699 | ||
|
|
c26d53f0d7 | ||
|
|
29f312dd69 | ||
|
|
72e6e23523 | ||
|
|
501046247d | ||
|
|
68cd0d4a11 | ||
|
|
56b819bfbc | ||
|
|
605422c60a | ||
|
|
cbbcc91498 | ||
|
|
b47d0e34ca | ||
|
|
bc12c69b54 | ||
|
|
1d42c9bc60 | ||
|
|
b71db93e03 | ||
|
|
6d5a7c8c40 | ||
|
|
8c1ed3ab82 | ||
|
|
f16dd7e028 | ||
|
|
08770fc246 | ||
|
|
0ca8587acc | ||
|
|
88e38d042f | ||
|
|
6a746a230a | ||
|
|
1e999352d3 | ||
|
|
6815d96617 | ||
|
|
61921498d0 | ||
|
|
4758d62fd4 | ||
|
|
1bc1a9748a | ||
|
|
de36476092 | ||
|
|
983a4cd286 | ||
|
|
b6abef94ff | ||
|
|
3d66766231 | ||
|
|
924122904e | ||
|
|
87691a86e5 | ||
|
|
8aa324fdc8 | ||
|
|
3c4d309f62 | ||
|
|
9bf531ca05 | ||
|
|
80354bdb06 | ||
|
|
fe2e0956ad | ||
|
|
cf71f3d627 | ||
|
|
da551c753f | ||
|
|
ae4681b787 | ||
|
|
c5a09a32a9 | ||
|
|
4785070ad3 | ||
|
|
0e0cd5bae5 | ||
|
|
44512f643e | ||
|
|
331c18d09a | ||
|
|
4fff92140e | ||
|
|
fc1d4b07df | ||
|
|
bd073343fa | ||
|
|
7c722dca5f | ||
|
|
4fce9cce86 | ||
|
|
415962da34 | ||
|
|
b41264d164 | ||
|
|
0f271dcf63 | ||
|
|
ead389aba2 | ||
|
|
698dc528f4 | ||
|
|
3dc6339025 | ||
|
|
15592c50f6 | ||
|
|
efb5e1bc93 | ||
|
|
ace5047782 | ||
|
|
03f23738bb | ||
|
|
24dbf29360 | ||
|
|
b6974ecbc9 | ||
|
|
12b2daccf3 | ||
|
|
f8d0c47bdb | ||
|
|
9f6fa94c91 | ||
|
|
123bb1af7b | ||
|
|
cf862d0fed | ||
|
|
57d105d498 | ||
|
|
5986edba1d | ||
|
|
c6e1a281f9 | ||
|
|
4f58695ed3 | ||
|
|
3facca4792 | ||
|
|
cf48ea8175 | ||
|
|
702fd95af3 | ||
|
|
94117a5d23 | ||
|
|
c3bd4463be | ||
|
|
9d7e8f1175 | ||
|
|
4047de4f6e | ||
|
|
49b9336558 | ||
|
|
d04a2aeccf | ||
|
|
33e01ae087 | ||
|
|
a09defec4c | ||
|
|
21c02cd3e5 | ||
|
|
5a36b71a80 | ||
|
|
9041570eba | ||
|
|
71b05fd4c2 | ||
|
|
598b609c45 | ||
|
|
b0e5209bde | ||
|
|
7fd536d7e0 | ||
|
|
be0951dfa4 | ||
|
|
86be961bb5 | ||
|
|
bb60d6def5 | ||
|
|
896e52255b | ||
|
|
12acc57967 | ||
|
|
6b9b57be22 | ||
|
|
d19f3ae890 | ||
|
|
e65ea3a45b | ||
|
|
87f77274de | ||
|
|
dded751757 | ||
|
|
d7b5545748 | ||
|
|
0747669972 | ||
|
|
f09bdef15e | ||
|
|
7119a16b5f | ||
|
|
cc514583cc | ||
|
|
021b174ee1 | ||
|
|
99cd9af7d5 | ||
|
|
8656386c4f | ||
|
|
c866abd842 | ||
|
|
1f5538f79d | ||
|
|
9ed906758e | ||
|
|
2f712c7456 | ||
|
|
d00ca18ac2 | ||
|
|
6cdb6894d4 | ||
|
|
ee0c920dff | ||
|
|
af6d5d3d7c | ||
|
|
cf9d57d56c | ||
|
|
4d95639001 | ||
|
|
3d3bfc6a4d | ||
|
|
41e2837df1 | ||
|
|
ecd5678527 | ||
|
|
28b9ba4334 | ||
|
|
b182ced1d6 | ||
|
|
fe18403935 | ||
|
|
981f59b163 | ||
|
|
e4eadb9a81 | ||
|
|
ff0681e4fd | ||
|
|
a2309a509d | ||
|
|
c49ffbfba8 | ||
|
|
e7533aa6f0 | ||
|
|
de8eaaba89 | ||
|
|
b2da44d6cb | ||
|
|
f6998a6a34 | ||
|
|
e3e0b5150b | ||
|
|
1df987a170 | ||
|
|
ddadb44a67 | ||
|
|
9ef44fc0b6 | ||
|
|
8c58e62597 | ||
|
|
8893e39712 | ||
|
|
3d6ae11340 | ||
|
|
a1f2600883 | ||
|
|
fcc85bdfbc | ||
|
|
7d08a3518a | ||
|
|
cc486a0354 | ||
|
|
fcc89c4bb2 | ||
|
|
a0431e226f | ||
|
|
89edfb6741 | ||
|
|
e69db83de5 | ||
|
|
06008b0444 |
54
.github/CONTRIBUTING.md
vendored
Normal file
54
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
Contribution Guidelines
|
||||
=======================
|
||||
|
||||
Contributions to cJSON are welcome. If you find a bug or want to improve cJSON in another way, pull requests are appreciated.
|
||||
|
||||
For bigger changes, in order to avoid wasted effort, please open an issue to discuss the technical details before creating a pull request.
|
||||
|
||||
The further sections explain the process in more detail and provides some guidelines on how contributions should look like.
|
||||
|
||||
Branches
|
||||
--------
|
||||
There are two branches to be aware of, the `master` and the `develop` branch. The `master` branch is reserved for the latest release, so only make pull requests to the `master` branch for small bug- or security fixes (these are usually just a few lines). In all other cases, please make a pull request to the `develop` branch.
|
||||
|
||||
Coding Style
|
||||
------------
|
||||
The coding style has been discussed in [#24](https://github.com/DaveGamble/cJSON/issues/24). The basics are:
|
||||
|
||||
* Use 4 tabs for indentation
|
||||
* No oneliners (conditions, loops, variable declarations ...)
|
||||
* Always use parenthesis for control structures
|
||||
* Don't implicitly rely on operator precedence, use round brackets in expressions. e.g. `(a > b) && (c < d)` instead of `a>b && c<d`
|
||||
* opening curly braces start in the next line
|
||||
* use spaces around operators
|
||||
* lines should not have trailing whitespace
|
||||
* use spaces between function parameters
|
||||
* use pronouncable variable names, not just a combination of letters
|
||||
|
||||
Example:
|
||||
|
||||
```c
|
||||
/* calculate the new length of the string in a printbuffer and update the offset */
|
||||
static void update_offset(printbuffer * const buffer)
|
||||
{
|
||||
const unsigned char *buffer_pointer = NULL;
|
||||
if ((buffer == NULL) || (buffer->buffer == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
buffer_pointer = buffer->buffer + buffer->offset;
|
||||
|
||||
buffer->offset += strlen((const char*)buffer_pointer);
|
||||
}
|
||||
```
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
cJSON uses the [Unity](https://github.com/ThrowTheSwitch/Unity) library for unit tests. The tests are located in the `tests` directory. In order to add a new test, either add it to one of the existing files (if it fits) or add a new C file for the test. That new file has to be added to the list of tests in `tests/CMakeLists.txt`.
|
||||
|
||||
All new features have to be covered by unit tests.
|
||||
|
||||
Other Notes
|
||||
-----------
|
||||
* Internal functions are to be declared static.
|
||||
* Wrap the return type of external function in the `CJSON_PUBLIC` macro.
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -8,3 +8,8 @@ test
|
||||
tags
|
||||
*.dylib
|
||||
build/
|
||||
cJSON_test
|
||||
cJSON_test_utils
|
||||
libcjson.so.*
|
||||
libcjson_utils.so.*
|
||||
*.orig
|
||||
|
||||
28
.travis.yml
Normal file
28
.travis.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
dist: trusty
|
||||
sudo: false
|
||||
language: c
|
||||
env:
|
||||
matrix:
|
||||
- VALGRIND=On SANITIZERS=Off
|
||||
- VALGRIND=Off SANITIZERS=Off
|
||||
- VALGRIND=Off SANITIZERS=On
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- valgrind
|
||||
- libasan0
|
||||
- lib32asan0
|
||||
# currently not supported on travis:
|
||||
# - libasan1
|
||||
# - libasan2
|
||||
# - libubsan0
|
||||
- llvm
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DENABLE_CJSON_UTILS=On -DENABLE_VALGRIND="${VALGRIND}" -DENABLE_SANITIZERS="${SANITIZERS}"
|
||||
- make
|
||||
- make test CTEST_OUTPUT_ON_FAILURE=On
|
||||
214
CHANGELOG.md
Normal file
214
CHANGELOG.md
Normal file
@@ -0,0 +1,214 @@
|
||||
1.5.1
|
||||
=====
|
||||
Fixes:
|
||||
------
|
||||
* Add gcc version guard to the Makefile (#164), thanks @juvasquezg
|
||||
* Fix incorrect free in `cJSON_Utils` if custom memory allocator is used (#166), thanks @prefetchnta
|
||||
|
||||
1.5.0
|
||||
=====
|
||||
Features:
|
||||
---------
|
||||
* cJSON finally prints numbers without losing precision (#153) thanks @DeboraG
|
||||
* `cJSON_Compare` recursively checks if two cJSON items contain the same values (#148)
|
||||
* Provide case sensitive versions of every function where it matters (#158, #159)
|
||||
* Added `cJSON_ReplaceItemViaPointer` and `cJSON_DetachItemViaPointer`
|
||||
* Added `cJSON_free` and `cJSON_malloc` that expose the internal configured memory allocators. (02a05eea4e6ba41811f130b322660bea8918e1a0)
|
||||
|
||||
|
||||
Enhancements:
|
||||
-------------
|
||||
* Parse into a buffer, this will allow parsing `\u0000` in the future (not quite yet though)
|
||||
* General simplifications and readability improvements
|
||||
* More unit tests
|
||||
* Update [unity](https://github.com/ThrowTheSwitch/Unity) testing library to 2.4.1
|
||||
* Add the [json-patch-tests](https://github.com/json-patch/json-patch-tests) test suite to test cJSON_Utils.
|
||||
* Move all tests from `test_utils.c` to unit tests with unity.
|
||||
|
||||
Fixes:
|
||||
------
|
||||
* Fix some warnings with the Microsoft compiler (#139) thanks @PawelWMS
|
||||
* Fix several bugs in cJSON_Utils, mostly found with [json-patch-tests](https://github.com/json-patch/json-patch-tests)
|
||||
* Prevent a stack overflow by specifying a maximum nesting depth `CJSON_NESTING_LIMIT`
|
||||
|
||||
Other Changes:
|
||||
--------------
|
||||
* Move generated files in the `library_config` subdirectory.
|
||||
|
||||
1.4.7
|
||||
=====
|
||||
Fixes:
|
||||
------
|
||||
* Fix `cJSONUtils_ApplyPatches`, it was completely broken and apparently nobody noticed (or at least reported it) (075a06f40bdc4f836c7dd7cad690d253a57cfc50)
|
||||
* Fix inconsistent prototype for `cJSON_GetObjectItemCaseSensitive` (51d3df6c9f7b56b860c8fb24abe7bab255cd4fa9) thanks @PawelWMS
|
||||
|
||||
1.4.6
|
||||
=====
|
||||
Fixes:
|
||||
------
|
||||
* Several corrections in the README
|
||||
* Making clear that `valueint` should not be written to
|
||||
* Fix overflow detection in `ensure` (2683d4d9873df87c4bdccc523903ddd78d1ad250)
|
||||
* Fix a potential null pointer dereference in cJSON_Utils (795c3acabed25c9672006b2c0f40be8845064827)
|
||||
* Replace incorrect `sizeof('\0')` with `sizeof("")` (84237ff48e69825c94261c624eb0376d0c328139)
|
||||
* Add caveats section to the README (50b3c30dfa89830f8f477ce33713500740ac3b79)
|
||||
* Make cJSON locale independent (#146) Thanks @peterh for reporting
|
||||
* Fix compiling without CMake with MSVC (#147) Thanks @dertuxmalwieder for reporting
|
||||
|
||||
1.4.5
|
||||
=====
|
||||
Fixes:
|
||||
------
|
||||
* Fix bug in `cJSON_SetNumberHelper`, thanks @mmkeeper (#138 ef34500693e8c4a2849d41a4bd66fd19c9ec46c2)
|
||||
* Workaround for internal compiler error in GCC 5.4.0 and 6.3.1 on x86 (2f65e80a3471d053fdc3f8aed23d01dd1782a5cb [GCC bugreport](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80097))
|
||||
|
||||
1.4.4
|
||||
=====
|
||||
Fixes:
|
||||
--------
|
||||
* Fix a theoretical integer overflow, (not sure if it is possible on actual hardware) e58f7ec027d00b7cdcbf63e518c1b5268b29b3da
|
||||
* Fix an off by one error (cc84a446be20cc283bafdc4d94c050ba1111ac02), thanks @gatzka
|
||||
* Double check the offset of the print buffer in `ensure` (1934059554b9a0971e00f79e96900f422cfdd114)
|
||||
|
||||
Improvements:
|
||||
-------------
|
||||
* Add a note in the header about required buffer size when using `cJSON_PrintPreallocated` (4bfb88009342fb568295a7f6dc4b7fee74fbf022)
|
||||
|
||||
1.4.3
|
||||
=====
|
||||
Fixes:
|
||||
------
|
||||
* Fix compilation of the tests on 32 bit PowerPC and potentially other systems (4ec6e76ea2eec16f54b58e8c95b4c734e59481e4)
|
||||
* Fix compilation with old GCC compilers (4.3+ were tested) (227d3398d6b967879761ebe02c1b63dbd6ea6e0d, 466eb8e3f8a65080f2b3ca4a79ab7b72bd539dba), see also #126
|
||||
|
||||
1.4.2
|
||||
=====
|
||||
Fixes:
|
||||
------
|
||||
* Fix minimum required cmake version (30e1e7af7c63db9b55f5a3cda977a6c032f0b132)
|
||||
* Fix detection of supported compiler flags (76e5296d0d05ceb3018a9901639e0e171b44a557)
|
||||
* Run `cJSON_test` and `cJSON_test_utils` along with unity tests (c597601cf151a757dcf800548f18034d4ddfe2cb)
|
||||
|
||||
1.4.1
|
||||
=====
|
||||
Fix: Make `print_number` abort with a failure in out of memory situations (cf1842dc6f64c49451a022308b4415e4d468be0a)
|
||||
|
||||
1.4.0
|
||||
=====
|
||||
Features
|
||||
--------
|
||||
* Functions to check the type of an item (#120)
|
||||
* Use dllexport on windows and fvisibility on Unix systems for public functions (#116), thanks @mjerris
|
||||
* Remove trailing zeroes from printed numbers (#123)
|
||||
* Expose the internal boolean type `cJSON_bool` in the header (2d3520e0b9d0eb870e8886e8a21c571eeddbb310)
|
||||
|
||||
Fixes
|
||||
-----
|
||||
* Fix handling of NULL pointers in `cJSON_ArrayForEach` (b47d0e34caaef298edfb7bd09a72cfff21d231ff)
|
||||
* Make it compile with GCC 7 (fix -Wimplicit-fallthrough warning) (9d07917feb1b613544a7513d19233d4c851ad7ad)
|
||||
|
||||
Other Improvements
|
||||
------------------
|
||||
* internally use realloc if available (#110)
|
||||
* builtin support for fuzzing with [afl](http://lcamtuf.coredump.cx/afl/) (#111)
|
||||
* unit tests for the print functions (#112)
|
||||
* Always use buffered printing (#113)
|
||||
* simplify the print functions (#114)
|
||||
* Add the compiler flags `-Wdouble-conversion`, `-Wparentheses` and `-Wcomma` (#122)
|
||||
|
||||
1.3.2
|
||||
=====
|
||||
Fix:
|
||||
----
|
||||
- Don't build the unity library if testing is disabled ( #121 ). Thanks @ffontaine
|
||||
|
||||
1.3.1
|
||||
=====
|
||||
Bugfix release that fixes an out of bounds read #118. This shouldn't have any security implications.
|
||||
|
||||
1.3.0
|
||||
=====
|
||||
This release includes a lot of rework in the parser and includes the Cunity unit testing framework, as well as some fixes. I increased the minor version number because there were quite a lot of internal changes.
|
||||
|
||||
Features:
|
||||
---------
|
||||
- New type for cJSON structs: `cJSON_Invalid` (#108)
|
||||
|
||||
Fixes:
|
||||
------
|
||||
- runtime checks for a lot of potential integer overflows
|
||||
- fix incorrect return in cJSON_PrintBuffered (cf9d57d56cac21fc59465b8d26cf29bf6d2a87b3)
|
||||
- fix several potential issues found by [Coverity](https://scan.coverity.com/projects/cjson)
|
||||
- fix potentially undefined behavior when assigning big numbers to `valueint` (41e2837df1b1091643aff073f2313f6ff3cc10f4)
|
||||
- Numbers exceeding `INT_MAX` or lower than `INT_MIN` will be explicitly assigned to `valueint` as `INT_MAX` and `INT_MIN` respectively (saturation on overflow).
|
||||
- fix the `cJSON_SetNumberValue` macro (87f77274de6b3af00fb9b9a7f3b900ef382296c2), this slightly changes the behavior, see commit message
|
||||
|
||||
Introduce unit tests
|
||||
--------------------
|
||||
|
||||
Started writing unit tests with the [Cunity](https://github.com/ThrowTheSwitch/Unity) testing framework. Currently this covers the parser functions.
|
||||
|
||||
Also:
|
||||
- Support for running the tests with [Valgrind](http://valgrind.org)
|
||||
- Support for compiling the tests with [AddressSanitizer](https://github.com/google/sanitizers) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html).
|
||||
- `travis.yml` file for running unit tests on travis. (not enabled for the repository yet though #102)
|
||||
|
||||
Simplifications
|
||||
---------------
|
||||
|
||||
After having unit tests for the parser function in place, I started refactoring the parser functions (as well as others) and making them easier to read and maintain.
|
||||
- Use `strtod` from the standard library for parsing numbers (074766997246481dfc72bfa78f07898a2716473f)
|
||||
- Use goto-fail in several parser functions (#100)
|
||||
- Rewrite/restructure all of the parsing functions to be easier to understand and have less code paths doing the same as another. (#109)
|
||||
- Simplify the buffer allocation strategy to always doubling the needed amount (9f6fa94c91a87b71e4c6868dbf2ce431a48517b0)
|
||||
- Combined `cJSON_AddItemToObject` and `cJSON_AddItemToObjectCS` to one function (cf862d0fed7f9407e4b046d78d3d8050d2080d12)
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
- Prevent the usage of incompatible C and header versions via preprocessor directive (123bb1af7bfae41d805337fef4b41045ef6c7d25)
|
||||
- Let CMake automatically detect compiler flags
|
||||
- Add new compiler flags (`-Wundef`, `-Wswitch-default`, `-Wconversion`, `-fstack-protector-strong`) (#98)
|
||||
- Change internal sizes from `int` to `size_t` (ecd5678527a6bc422da694e5be9e9979878fe6a0)
|
||||
- Change internal strings from `char*` to `unsigned char*` (28b9ba4334e0f7309e867e874a31f395c0ac2474)
|
||||
- Add `const` in more places
|
||||
|
||||
1.2.1
|
||||
=====
|
||||
Fixes:
|
||||
------
|
||||
- Fixes a potential null pointer dereference in cJSON_Utils, discovered using clang's static analyzer by @bnason-nf (#96)
|
||||
|
||||
1.2.0
|
||||
=====
|
||||
Features:
|
||||
---------
|
||||
- Add a new type of cJSON item for raw JSON and support printing it. Thanks @loigu (#65, #90)
|
||||
|
||||
Fixes:
|
||||
------
|
||||
- Compiler warning if const is casted away, Thanks @gatzka (#83)
|
||||
- Fix compile error with strict-overflow on PowerPC, (#85)
|
||||
- Fix typo in the README, thanks @MicroJoe (#88)
|
||||
- Add compile flag for compatibility with C++ compilers
|
||||
|
||||
1.1.0
|
||||
=====
|
||||
- Add a function `cJSON_PrintPreallocated` to print to a preallocated buffer, thanks @ChisholmKyle (#72)
|
||||
- More compiler warnings when using Clang or GCC, thanks @gatzka (#75, #78)
|
||||
- fixed a memory leak in `cJSON_Duplicate`, thanks @alperakcan (#81)
|
||||
- fix the `ENABLE_CUSTOM_COMPILER_FLAGS` cmake option
|
||||
|
||||
1.0.2
|
||||
=====
|
||||
Rename internal boolean type, see #71.
|
||||
|
||||
1.0.1
|
||||
=====
|
||||
Small bugfix release.
|
||||
- Fixes a bug with the use of the cJSON structs type in cJSON_Utils, see d47339e2740360e6e0994527d5e4752007480f3a
|
||||
- improve code readability
|
||||
- initialize all variables
|
||||
|
||||
1.0.0
|
||||
=====
|
||||
This is the first official versioned release of cJSON. It provides an API version for the shared library and improved Makefile and CMake build files.
|
||||
105
CMakeLists.txt
105
CMakeLists.txt
@@ -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 5)
|
||||
set(PROJECT_VERSION_PATCH 1)
|
||||
set(CJSON_VERSION_SO 1)
|
||||
set(CJSON_UTILS_VERSION_SO 1)
|
||||
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
|
||||
|
||||
set(custom_compiler_flags)
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
option(ENABLE_CUSTOM_COMPILER_FLAGS "Enables custom compiler flags for Clang and GCC" ON)
|
||||
if (ENABLE_CUSTOM_COMPILER_FLAGS)
|
||||
if(("${CMAKE_C_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang"))
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c89 -pedantic -Wall -Wextra -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes -Wstrict-overflow=2")
|
||||
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}")
|
||||
@@ -39,7 +103,7 @@ if (NOT WIN32)
|
||||
target_link_libraries("${CJSON_LIB}" m)
|
||||
endif()
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libcjson.pc.in"
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/library_config/libcjson.pc.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/libcjson.pc" @ONLY)
|
||||
|
||||
install(FILES cJSON.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/cjson")
|
||||
@@ -66,7 +130,7 @@ if(ENABLE_CJSON_UTILS)
|
||||
add_library("${CJSON_UTILS_LIB}" "${HEADERS_UTILS}" "${SOURCES_UTILS}")
|
||||
target_link_libraries("${CJSON_UTILS_LIB}" "${CJSON_LIB}")
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libcjson_utils.pc.in"
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/library_config/libcjson_utils.pc.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/libcjson_utils.pc" @ONLY)
|
||||
|
||||
install(TARGETS "${CJSON_UTILS_LIB}" DESTINATION "${CMAKE_INSTALL_LIBDIR}" EXPORT "${CJSON_UTILS_LIB}")
|
||||
@@ -85,10 +149,10 @@ endif()
|
||||
|
||||
# create the other package config files
|
||||
configure_file(
|
||||
cJSONConfig.cmake.in
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/library_config/cJSONConfig.cmake.in"
|
||||
${PROJECT_BINARY_DIR}/cJSONConfig.cmake @ONLY)
|
||||
configure_file(
|
||||
cJSONConfigVersion.cmake.in
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/library_config/cJSONConfigVersion.cmake.in"
|
||||
${PROJECT_BINARY_DIR}/cJSONConfigVersion.cmake @ONLY)
|
||||
|
||||
# Install package config files
|
||||
@@ -98,13 +162,28 @@ 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}")
|
||||
|
||||
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} 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()
|
||||
|
||||
#"check" target that automatically builds everything and runs the tests
|
||||
add_custom_target(check
|
||||
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
|
||||
DEPENDS ${TEST_CJSON})
|
||||
endif()
|
||||
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(fuzzing)
|
||||
|
||||
@@ -6,6 +6,7 @@ Contributors
|
||||
* [Anton Sergeev](https://github.com/anton-sergeev)
|
||||
* [Christian Schulze](https://github.com/ChristianSch)
|
||||
* [Dave Gamble](https://github.com/DaveGamble)
|
||||
* [Debora Grosse](https://github.com/DeboraG)
|
||||
* [dieyushi](https://github.com/dieyushi)
|
||||
* [Dongwen Huang (黄东文)](https://github.com/DongwenHuang)
|
||||
* Eswar Yaganti
|
||||
@@ -14,16 +15,23 @@ Contributors
|
||||
* Ian Mobley
|
||||
* Irwan Djadjadi
|
||||
* [IvanVoid](https://github.com/npi3pak)
|
||||
* [Jiri Zouhar](https://github.com/loigu)
|
||||
* [Jonathan Fether](https://github.com/jfether)
|
||||
* [Julián Vásquez](https://github.com/juvasquezg)
|
||||
* [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)
|
||||
* [Mike Robinson](https://github.com/mhrobinson)
|
||||
* Paulo Antonio Alvarez
|
||||
* [Pawel Winogrodzki](https://github.com/PawelWMS)
|
||||
* [prefetchnta](https://github.com/prefetchnta)
|
||||
* [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)
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
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
|
||||
|
||||
19
Makefile
19
Makefile
@@ -3,14 +3,12 @@ UTILS_OBJ = cJSON_Utils.o
|
||||
CJSON_LIBNAME = libcjson
|
||||
UTILS_LIBNAME = libcjson_utils
|
||||
CJSON_TEST = cJSON_test
|
||||
UTILS_TEST = cJSON_test_utils
|
||||
|
||||
CJSON_TEST_SRC = cJSON.c test.c
|
||||
UTILS_TEST_SRC = cJSON.c cJSON_Utils.c test_utils.c
|
||||
|
||||
LDLIBS = -lm
|
||||
|
||||
LIBVERSION = 1.1.0
|
||||
LIBVERSION = 1.5.1
|
||||
CJSON_SOVERSION = 1
|
||||
UTILS_SOVERSION = 1
|
||||
|
||||
@@ -23,7 +21,17 @@ 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)
|
||||
# validate gcc version for use fstack-protector-strong
|
||||
MIN_GCC_VERSION = "4.9"
|
||||
GCC_VERSION := "`gcc -dumpversion`"
|
||||
IS_GCC_ABOVE_MIN_VERSION := $(shell expr "$(GCC_VERSION)" ">=" "$(MIN_GCC_VERSION)")
|
||||
ifeq "$(IS_GCC_ABOVE_MIN_VERSION)" "1"
|
||||
CFLAGS += -fstack-protector-strong
|
||||
else
|
||||
CFLAGS += -fstack-protector
|
||||
endif
|
||||
|
||||
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 $(CFLAGS)
|
||||
|
||||
uname := $(shell sh -c 'uname -s 2>/dev/null || echo false')
|
||||
|
||||
@@ -71,9 +79,6 @@ test: tests
|
||||
#cJSON
|
||||
$(CJSON_TEST): $(CJSON_TEST_SRC) cJSON.h
|
||||
$(CC) $(R_CFLAGS) $(CJSON_TEST_SRC) -o $@ $(LDLIBS) -I.
|
||||
#cJSON_Utils
|
||||
$(UTILS_TEST): $(UTILS_TEST_SRC) cJSON.h cJSON_Utils.h
|
||||
$(CC) $(R_CFLAGS) $(UTILS_TEST_SRC) -o $@ $(LDLIBS) -I.
|
||||
|
||||
#static libraries
|
||||
#cJSON
|
||||
|
||||
87
README.md
87
README.md
@@ -9,11 +9,14 @@ Ultralightweight JSON parser in ANSI C.
|
||||
* [Building](#building)
|
||||
* [Some JSON](#some-json)
|
||||
* [Here's the structure](#heres-the-structure)
|
||||
* [Caveats](#caveats)
|
||||
* [Enjoy cJSON!](#enjoy-cjson)
|
||||
|
||||
## License
|
||||
|
||||
> Copyright (c) 2009-2016 Dave Gamble
|
||||
MIT License
|
||||
|
||||
> 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
|
||||
@@ -62,7 +65,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 +82,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 +95,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.
|
||||
|
||||
@@ -135,14 +138,20 @@ This is an object. We're in C. We don't have objects. But we do have structs.
|
||||
What's the framerate?
|
||||
|
||||
```c
|
||||
cJSON *format = cJSON_GetObjectItem(root, "format");
|
||||
int framerate = cJSON_GetObjectItem(format, "frame rate")->valueint;
|
||||
cJSON *format = cJSON_GetObjectItemCaseSensitive(root, "format");
|
||||
cJSON *framerate_item = cJSON_GetObjectItemCaseSensitive(format, "frame rate");
|
||||
double framerate = 0;
|
||||
if (cJSON_IsNumber(framerate_item))
|
||||
{
|
||||
framerate = framerate_item->valuedouble;
|
||||
}
|
||||
```
|
||||
|
||||
Want to change the framerate?
|
||||
|
||||
```c
|
||||
cJSON_GetObjectItem(format, "frame rate")->valueint = 25;
|
||||
cJSON *framerate_item = cJSON_GetObjectItemCaseSensitive(format, "frame rate");
|
||||
cJSON_SetNumberValue(framerate_item, 25);
|
||||
```
|
||||
|
||||
Back to disk?
|
||||
@@ -201,7 +210,7 @@ typedef struct cJSON {
|
||||
int type;
|
||||
|
||||
char *valuestring;
|
||||
int valueint;
|
||||
int valueint; /* writing to valueint is DEPRECATED, please use cJSON_SetNumberValue instead */
|
||||
double valuedouble;
|
||||
|
||||
char *string;
|
||||
@@ -217,8 +226,7 @@ A `child` entry will have `prev == 0`, but next potentially points on. The last
|
||||
The type expresses *Null*/*True*/*False*/*Number*/*String*/*Array*/*Object*, all of which are `#defined` in
|
||||
`cJSON.h`.
|
||||
|
||||
A *Number* has `valueint` and `valuedouble`. If you're expecting an `int`, read `valueint`, if not read
|
||||
`valuedouble`.
|
||||
A *Number* has `valueint` and `valuedouble`. `valueint` is a relict of the past, so always use `valuedouble`.
|
||||
|
||||
Any entry which is in the linked list which is the child of an object will have a `string`
|
||||
which is the "name" of the entry. When I said "name" in the above example, that's `string`.
|
||||
@@ -235,8 +243,8 @@ void parse_and_callback(cJSON *item, const char *prefix)
|
||||
{
|
||||
while (item)
|
||||
{
|
||||
char *newprefix = malloc(strlen(prefix) + strlen(item->name) + 2);
|
||||
sprintf(newprefix, "%s/%s", prefix, item->name);
|
||||
char *newprefix = malloc(strlen(prefix) + strlen(item->string) + 2);
|
||||
sprintf(newprefix, "%s/%s", prefix, item->string);
|
||||
int dorecurse = callback(newprefix, item->type, item);
|
||||
if (item->child && dorecurse)
|
||||
{
|
||||
@@ -259,22 +267,22 @@ int callback(const char *name, int type, cJSON *item)
|
||||
{
|
||||
/* populate name */
|
||||
}
|
||||
else if (!strcmp(name, "format/type")
|
||||
else if (!strcmp(name, "format/type"))
|
||||
{
|
||||
/* handle "rect" */ }
|
||||
else if (!strcmp(name, "format/width")
|
||||
else if (!strcmp(name, "format/width"))
|
||||
{
|
||||
/* 800 */
|
||||
}
|
||||
else if (!strcmp(name, "format/height")
|
||||
else if (!strcmp(name, "format/height"))
|
||||
{
|
||||
/* 600 */
|
||||
}
|
||||
else if (!strcmp(name, "format/interlace")
|
||||
else if (!strcmp(name, "format/interlace"))
|
||||
{
|
||||
/* false */
|
||||
}
|
||||
else if (!strcmp(name, "format/frame rate")
|
||||
else if (!strcmp(name, "format/frame rate"))
|
||||
{
|
||||
/* 24 */
|
||||
}
|
||||
@@ -367,6 +375,45 @@ The `test.c` code shows how to handle a bunch of typical cases. If you uncomment
|
||||
the code, it'll load, parse and print a bunch of test files, also from [json.org](http://json.org),
|
||||
which are more complex than I'd care to try and stash into a `const char array[]`.
|
||||
|
||||
### Caveats
|
||||
|
||||
#### Zero Character
|
||||
|
||||
cJSON doesn't support strings that contain the zero character `'\0'` or `\u0000`. This is impossible with the current API because strings are zero terminated.
|
||||
|
||||
#### Character Encoding
|
||||
|
||||
cJSON only supports UTF-8 encoded input and will always produce UTF-8 as output (If the input contained invalid UTF-8, it will most likely propagate it through to the output, thereby making the output non-valid UTF-8).
|
||||
|
||||
#### C Standard
|
||||
|
||||
cJSON is written in ANSI C (or C89, C90). If your compiler or C library doesn't follow this standard, correct behavior is not guaranteed.
|
||||
|
||||
NOTE: ANSI C is not C++ therefore it shouldn't be compiled by a C++ compiler. You can compile it with a C compiler and link it with your C++ code however. Although compiling with a C++ compiler might work, correct behavior is not guaranteed.
|
||||
|
||||
#### Floating Point Numbers
|
||||
|
||||
cJSON does not officially support any `double` implementations other than IEE754 double precision floating point numbers. It might still work with other implementations but bugs with these will be considered invalid.
|
||||
|
||||
The maximum length of a floating point literal that cJSON supports is currently 63 characters.
|
||||
|
||||
#### Deep Nesting Of Arrays And Objects
|
||||
|
||||
cJSON doesn't support arrays and objects that are nested too deeply because this would result in a stack overflow. To prevent this cJSON limits the depth to `CJSON_NESTING_LIMIT` which is 1000 by default but can be changed at compile time.
|
||||
|
||||
#### Thread Safety
|
||||
|
||||
In general cJSON is **not thread safe**.
|
||||
|
||||
However it is thread safe under the following conditions:
|
||||
* `cJSON_GetErrorPtr` is never used (the `return_parse_end` parameter of `cJSON_ParseWithOpts` can be used instead)
|
||||
* `cJSON_InitHooks` is only ever called before using cJSON in any threads.
|
||||
* `setlocale` is never called before all calls to cJSON functions have returned.
|
||||
|
||||
#### Case Sensitivity
|
||||
|
||||
When cJSON was originally created, it didn't follow the JSON standard and didn't make a distinction between uppercase and lowercase letters. If you want the correct, standard compliant, behavior, you need to use the `CaseSensitive` functions where available.
|
||||
|
||||
# Enjoy cJSON!
|
||||
|
||||
- Dave Gamble, Aug 2009
|
||||
|
||||
193
cJSON.h
193
cJSON.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
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
|
||||
@@ -28,9 +28,15 @@ extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* project version */
|
||||
#define CJSON_VERSION_MAJOR 1
|
||||
#define CJSON_VERSION_MINOR 5
|
||||
#define CJSON_VERSION_PATCH 1
|
||||
|
||||
#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,9 +61,9 @@ 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 */
|
||||
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
||||
int valueint;
|
||||
/* The item's number, if type==cJSON_Number */
|
||||
double valuedouble;
|
||||
@@ -71,82 +78,161 @@ 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 (default)
|
||||
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
/* export symbols by default, this is necessary for copy pasting the C and header file */
|
||||
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_EXPORT_SYMBOLS
|
||||
#endif
|
||||
|
||||
#if defined(CJSON_HIDE_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) type __stdcall
|
||||
#elif defined(CJSON_EXPORT_SYMBOLS)
|
||||
#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall
|
||||
#elif defined(CJSON_IMPORT_SYMBOLS)
|
||||
#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
|
||||
|
||||
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
||||
* This is to prevent stack overflows. */
|
||||
#ifndef CJSON_NESTING_LIMIT
|
||||
#define CJSON_NESTING_LIMIT 1000
|
||||
#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);
|
||||
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
|
||||
extern 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);
|
||||
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||
/* Render a cJSON entity to text for transfer/storage. */
|
||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
/* Render a cJSON entity to text for transfer/storage without any formatting. */
|
||||
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: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||
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 index);
|
||||
/* 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 * const object, const char * const string);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const 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_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
||||
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(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(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(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||
CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(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. */
|
||||
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
||||
|
||||
|
||||
/* 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 +241,20 @@ 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)
|
||||
|
||||
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
||||
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||
CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
1698
cJSON_Utils.c
1698
cJSON_Utils.c
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,40 @@
|
||||
/*
|
||||
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 "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 * const object, const char *pointer);
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
|
||||
|
||||
/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
|
||||
cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to);
|
||||
/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const 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 * const array, const char * const operation, const char * const path, const cJSON * const value);
|
||||
/* Returns 0 for success. */
|
||||
int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
|
||||
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
|
||||
CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
|
||||
|
||||
/*
|
||||
// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
|
||||
@@ -33,12 +59,16 @@ 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, const cJSON * const patch);
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
|
||||
/* generates a patch to move from -> to */
|
||||
cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to);
|
||||
/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
|
||||
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const 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(const cJSON * const object, const cJSON * const target);
|
||||
|
||||
/* Sorts the members of the object into alphabetical order. */
|
||||
void cJSONUtils_SortObject(cJSON *object);
|
||||
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
|
||||
CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
|
||||
|
||||
1
fuzzing/.gitignore
vendored
Normal file
1
fuzzing/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
afl-build
|
||||
28
fuzzing/CMakeLists.txt
Normal file
28
fuzzing/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
option(ENABLE_FUZZING "Create executables and targets for fuzzing cJSON with afl." Off)
|
||||
if (ENABLE_FUZZING)
|
||||
find_program(AFL_FUZZ afl-fuzz)
|
||||
if ("${AFL_FUZZ}" MATCHES "AFL_FUZZ-NOTFOUND")
|
||||
message(FATAL_ERROR "Couldn't find afl-fuzz.")
|
||||
endif()
|
||||
|
||||
add_executable(afl-main afl.c)
|
||||
target_link_libraries(afl-main "${CJSON_LIB}")
|
||||
|
||||
if (NOT ENABLE_SANITIZERS)
|
||||
message(FATAL_ERROR "Enable sanitizers with -DENABLE_SANITIZERS=On to do fuzzing.")
|
||||
endif()
|
||||
|
||||
option(ENABLE_FUZZING_PRINT "Fuzz printing functions together with parser." On)
|
||||
set(fuzz_print_parameter "no")
|
||||
if (ENABLE_FUZZING_PRINT)
|
||||
set(fuzz_print_parameter "yes")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error")
|
||||
|
||||
add_custom_target(afl
|
||||
COMMAND "${AFL_FUZZ}" -i "${CMAKE_CURRENT_SOURCE_DIR}/inputs" -o "${CMAKE_CURRENT_BINARY_DIR}/findings" -x "${CMAKE_CURRENT_SOURCE_DIR}/json.dict" -- "${CMAKE_CURRENT_BINARY_DIR}/afl-main" "@@" "${fuzz_print_parameter}"
|
||||
DEPENDS afl-main)
|
||||
|
||||
|
||||
endif()
|
||||
5
fuzzing/afl-prepare-linux.sh
Executable file
5
fuzzing/afl-prepare-linux.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
echo core | sudo tee /proc/sys/kernel/core_pattern
|
||||
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
||||
176
fuzzing/afl.c
Normal file
176
fuzzing/afl.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../cJSON.h"
|
||||
|
||||
static char *read_file(const char *filename)
|
||||
{
|
||||
FILE *file = NULL;
|
||||
long length = 0;
|
||||
char *content = NULL;
|
||||
size_t read_chars = 0;
|
||||
|
||||
/* open in read binary mode */
|
||||
file = fopen(filename, "rb");
|
||||
if (file == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* get the length */
|
||||
if (fseek(file, 0, SEEK_END) != 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
length = ftell(file);
|
||||
if (length < 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
if (fseek(file, 0, SEEK_SET) != 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* allocate content buffer */
|
||||
content = (char*)malloc((size_t)length + sizeof(""));
|
||||
if (content == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* read the file into memory */
|
||||
read_chars = fread(content, sizeof(char), (size_t)length, file);
|
||||
if ((long)read_chars != length)
|
||||
{
|
||||
free(content);
|
||||
content = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
content[read_chars] = '\0';
|
||||
|
||||
|
||||
cleanup:
|
||||
if (file != NULL)
|
||||
{
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char *filename = NULL;
|
||||
cJSON *item = NULL;
|
||||
char *json = NULL;
|
||||
int status = EXIT_FAILURE;
|
||||
char *printed_json = NULL;
|
||||
|
||||
if ((argc < 2) || (argc > 3))
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf("%s input_file [enable_printing]\n", argv[0]);
|
||||
printf("\t input_file: file containing the test data\n");
|
||||
printf("\t enable_printing: print after parsing, 'yes' or 'no', defaults to 'no'\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
filename = argv[1];
|
||||
|
||||
#if __AFL_HAVE_MANUAL_CONTROL
|
||||
while (__AFL_LOOP(1000))
|
||||
{
|
||||
#endif
|
||||
status = EXIT_SUCCESS;
|
||||
|
||||
json = read_file(filename);
|
||||
if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0'))
|
||||
{
|
||||
status = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
item = cJSON_Parse(json + 2);
|
||||
if (item == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((argc == 3) && (strncmp(argv[2], "yes", 3) == 0))
|
||||
{
|
||||
int do_format = 0;
|
||||
if (json[1] == 'f')
|
||||
{
|
||||
do_format = 1;
|
||||
}
|
||||
|
||||
if (json[0] == 'b')
|
||||
{
|
||||
/* buffered printing */
|
||||
printed_json = cJSON_PrintBuffered(item, 1, do_format);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* unbuffered printing */
|
||||
if (do_format)
|
||||
{
|
||||
printed_json = cJSON_Print(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
printed_json = cJSON_PrintUnformatted(item);
|
||||
}
|
||||
}
|
||||
if (printed_json == NULL)
|
||||
{
|
||||
status = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
printf("%s\n", printed_json);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (item != NULL)
|
||||
{
|
||||
cJSON_Delete(item);
|
||||
item = NULL;
|
||||
}
|
||||
if (json != NULL)
|
||||
{
|
||||
free(json);
|
||||
json = NULL;
|
||||
}
|
||||
if (printed_json != NULL)
|
||||
{
|
||||
free(printed_json);
|
||||
printed_json = NULL;
|
||||
}
|
||||
#if __AFL_HAVE_MANUAL_CONTROL
|
||||
}
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
9
fuzzing/afl.sh
Executable file
9
fuzzing/afl.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkdir -p afl-build || exit 1
|
||||
cd afl-build || exit 1
|
||||
#cleanup
|
||||
rm -r -- *
|
||||
|
||||
CC=afl-clang-fast cmake ../.. -DENABLE_FUZZING=On -DENABLE_SANITIZERS=On -DBUILD_SHARED_LIBS=Off
|
||||
make afl
|
||||
22
fuzzing/inputs/test1
Normal file
22
fuzzing/inputs/test1
Normal file
@@ -0,0 +1,22 @@
|
||||
bf{
|
||||
"glossary": {
|
||||
"title": "example glossary",
|
||||
"GlossDiv": {
|
||||
"title": "S",
|
||||
"GlossList": {
|
||||
"GlossEntry": {
|
||||
"ID": "SGML",
|
||||
"SortAs": "SGML",
|
||||
"GlossTerm": "Standard Generalized Markup Language",
|
||||
"Acronym": "SGML",
|
||||
"Abbrev": "ISO 8879:1986",
|
||||
"GlossDef": {
|
||||
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||
"GlossSeeAlso": ["GML", "XML"]
|
||||
},
|
||||
"GlossSee": "markup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
fuzzing/inputs/test10
Normal file
1
fuzzing/inputs/test10
Normal file
@@ -0,0 +1 @@
|
||||
bf["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
8
fuzzing/inputs/test11
Normal file
8
fuzzing/inputs/test11
Normal file
@@ -0,0 +1,8 @@
|
||||
bf{
|
||||
"name": "Jack (\"Bee\") Nimble",
|
||||
"format": {"type": "rect",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"interlace": false,"frame rate": 24
|
||||
}
|
||||
}
|
||||
11
fuzzing/inputs/test2
Normal file
11
fuzzing/inputs/test2
Normal file
@@ -0,0 +1,11 @@
|
||||
bf{"menu": {
|
||||
"id": "file",
|
||||
"value": "File",
|
||||
"popup": {
|
||||
"menuitem": [
|
||||
{"value": "New", "onclick": "CreateNewDoc()"},
|
||||
{"value": "Open", "onclick": "OpenDoc()"},
|
||||
{"value": "Close", "onclick": "CloseDoc()"}
|
||||
]
|
||||
}
|
||||
}}
|
||||
26
fuzzing/inputs/test3
Normal file
26
fuzzing/inputs/test3
Normal file
@@ -0,0 +1,26 @@
|
||||
bf{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
||||
26
fuzzing/inputs/test3.bu
Normal file
26
fuzzing/inputs/test3.bu
Normal file
@@ -0,0 +1,26 @@
|
||||
bu{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
||||
26
fuzzing/inputs/test3.uf
Normal file
26
fuzzing/inputs/test3.uf
Normal file
@@ -0,0 +1,26 @@
|
||||
uf{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
||||
26
fuzzing/inputs/test3.uu
Normal file
26
fuzzing/inputs/test3.uu
Normal file
@@ -0,0 +1,26 @@
|
||||
uu{"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}}
|
||||
88
fuzzing/inputs/test4
Normal file
88
fuzzing/inputs/test4
Normal file
@@ -0,0 +1,88 @@
|
||||
bf{"web-app": {
|
||||
"servlet": [
|
||||
{
|
||||
"servlet-name": "cofaxCDS",
|
||||
"servlet-class": "org.cofax.cds.CDSServlet",
|
||||
"init-param": {
|
||||
"configGlossary:installationAt": "Philadelphia, PA",
|
||||
"configGlossary:adminEmail": "ksm@pobox.com",
|
||||
"configGlossary:poweredBy": "Cofax",
|
||||
"configGlossary:poweredByIcon": "/images/cofax.gif",
|
||||
"configGlossary:staticPath": "/content/static",
|
||||
"templateProcessorClass": "org.cofax.WysiwygTemplate",
|
||||
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
|
||||
"templatePath": "templates",
|
||||
"templateOverridePath": "",
|
||||
"defaultListTemplate": "listTemplate.htm",
|
||||
"defaultFileTemplate": "articleTemplate.htm",
|
||||
"useJSP": false,
|
||||
"jspListTemplate": "listTemplate.jsp",
|
||||
"jspFileTemplate": "articleTemplate.jsp",
|
||||
"cachePackageTagsTrack": 200,
|
||||
"cachePackageTagsStore": 200,
|
||||
"cachePackageTagsRefresh": 60,
|
||||
"cacheTemplatesTrack": 100,
|
||||
"cacheTemplatesStore": 50,
|
||||
"cacheTemplatesRefresh": 15,
|
||||
"cachePagesTrack": 200,
|
||||
"cachePagesStore": 100,
|
||||
"cachePagesRefresh": 10,
|
||||
"cachePagesDirtyRead": 10,
|
||||
"searchEngineListTemplate": "forSearchEnginesList.htm",
|
||||
"searchEngineFileTemplate": "forSearchEngines.htm",
|
||||
"searchEngineRobotsDb": "WEB-INF/robots.db",
|
||||
"useDataStore": true,
|
||||
"dataStoreClass": "org.cofax.SqlDataStore",
|
||||
"redirectionClass": "org.cofax.SqlRedirection",
|
||||
"dataStoreName": "cofax",
|
||||
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
|
||||
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
|
||||
"dataStoreUser": "sa",
|
||||
"dataStorePassword": "dataStoreTestQuery",
|
||||
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
|
||||
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
|
||||
"dataStoreInitConns": 10,
|
||||
"dataStoreMaxConns": 100,
|
||||
"dataStoreConnUsageLimit": 100,
|
||||
"dataStoreLogLevel": "debug",
|
||||
"maxUrlLength": 500}},
|
||||
{
|
||||
"servlet-name": "cofaxEmail",
|
||||
"servlet-class": "org.cofax.cds.EmailServlet",
|
||||
"init-param": {
|
||||
"mailHost": "mail1",
|
||||
"mailHostOverride": "mail2"}},
|
||||
{
|
||||
"servlet-name": "cofaxAdmin",
|
||||
"servlet-class": "org.cofax.cds.AdminServlet"},
|
||||
|
||||
{
|
||||
"servlet-name": "fileServlet",
|
||||
"servlet-class": "org.cofax.cds.FileServlet"},
|
||||
{
|
||||
"servlet-name": "cofaxTools",
|
||||
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
|
||||
"init-param": {
|
||||
"templatePath": "toolstemplates/",
|
||||
"log": 1,
|
||||
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
|
||||
"logMaxSize": "",
|
||||
"dataLog": 1,
|
||||
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
|
||||
"dataLogMaxSize": "",
|
||||
"removePageCache": "/content/admin/remove?cache=pages&id=",
|
||||
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
|
||||
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
|
||||
"lookInContext": 1,
|
||||
"adminGroupID": 4,
|
||||
"betaServer": true}}],
|
||||
"servlet-mapping": {
|
||||
"cofaxCDS": "/",
|
||||
"cofaxEmail": "/cofaxutil/aemail/*",
|
||||
"cofaxAdmin": "/admin/*",
|
||||
"fileServlet": "/static/*",
|
||||
"cofaxTools": "/tools/*"},
|
||||
|
||||
"taglib": {
|
||||
"taglib-uri": "cofax.tld",
|
||||
"taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
|
||||
27
fuzzing/inputs/test5
Normal file
27
fuzzing/inputs/test5
Normal file
@@ -0,0 +1,27 @@
|
||||
bf{"menu": {
|
||||
"header": "SVG Viewer",
|
||||
"items": [
|
||||
{"id": "Open"},
|
||||
{"id": "OpenNew", "label": "Open New"},
|
||||
null,
|
||||
{"id": "ZoomIn", "label": "Zoom In"},
|
||||
{"id": "ZoomOut", "label": "Zoom Out"},
|
||||
{"id": "OriginalView", "label": "Original View"},
|
||||
null,
|
||||
{"id": "Quality"},
|
||||
{"id": "Pause"},
|
||||
{"id": "Mute"},
|
||||
null,
|
||||
{"id": "Find", "label": "Find..."},
|
||||
{"id": "FindAgain", "label": "Find Again"},
|
||||
{"id": "Copy"},
|
||||
{"id": "CopyAgain", "label": "Copy Again"},
|
||||
{"id": "CopySVG", "label": "Copy SVG"},
|
||||
{"id": "ViewSVG", "label": "View SVG"},
|
||||
{"id": "ViewSource", "label": "View Source"},
|
||||
{"id": "SaveAs", "label": "Save As"},
|
||||
null,
|
||||
{"id": "Help"},
|
||||
{"id": "About", "label": "About Adobe CVG Viewer..."}
|
||||
]
|
||||
}}
|
||||
16
fuzzing/inputs/test6
Normal file
16
fuzzing/inputs/test6
Normal file
@@ -0,0 +1,16 @@
|
||||
bf<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
html, body, iframe { margin: 0; padding: 0; height: 100%; }
|
||||
iframe { display: block; width: 100%; border: none; }
|
||||
</style>
|
||||
<title>Application Error</title>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="//s3.amazonaws.com/heroku_pages/error.html">
|
||||
<p>Application Error</p>
|
||||
</iframe>
|
||||
</body>
|
||||
</html>
|
||||
22
fuzzing/inputs/test7
Normal file
22
fuzzing/inputs/test7
Normal file
@@ -0,0 +1,22 @@
|
||||
bf[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]
|
||||
13
fuzzing/inputs/test8
Normal file
13
fuzzing/inputs/test8
Normal file
@@ -0,0 +1,13 @@
|
||||
bf{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http:/*www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
}
|
||||
}
|
||||
5
fuzzing/inputs/test9
Normal file
5
fuzzing/inputs/test9
Normal file
@@ -0,0 +1,5 @@
|
||||
bf[
|
||||
[0, -1, 0],
|
||||
[1, 0, 0],
|
||||
[0, 0, 1]
|
||||
]
|
||||
47
fuzzing/json.dict
Normal file
47
fuzzing/json.dict
Normal file
@@ -0,0 +1,47 @@
|
||||
#
|
||||
# AFL dictionary for JSON
|
||||
# -----------------------------
|
||||
#
|
||||
|
||||
object_start="{"
|
||||
object_end="}"
|
||||
object_empty="{}"
|
||||
object_one_element="{\"one\":1}"
|
||||
object_two_elements="{\"1\":1,\"2\":2}"
|
||||
object_separator=":"
|
||||
|
||||
array_start="["
|
||||
array_end="]"
|
||||
array_empty="[]"
|
||||
array_one_element="[1]"
|
||||
array_two_elements="[1,2]"
|
||||
|
||||
separator=","
|
||||
|
||||
escape_sequence_b="\\b"
|
||||
escape_sequence_f="\\f"
|
||||
escape_sequence_n="\\n"
|
||||
escape_sequence_r="\\r"
|
||||
escape_sequence_t="\\t"
|
||||
escape_sequence_quote="\\\""
|
||||
escape_sequence_backslash="\\\\"
|
||||
escapce_sequence_slash="\\/"
|
||||
escpae_sequence_utf16_base="\\u"
|
||||
escape_sequence_utf16="\\u12ab"
|
||||
|
||||
number_integer="1"
|
||||
number_double="1.0"
|
||||
number_negative_integer="-1"
|
||||
number_negative_double="-1.0"
|
||||
number_engineering1="1e1"
|
||||
number_engineering2="1e-1"
|
||||
number_positive_integer="+1"
|
||||
number_positive_double="+1.0"
|
||||
number_e="e"
|
||||
number_plus="+"
|
||||
number_minus="-"
|
||||
number_separator="."
|
||||
|
||||
null="null"
|
||||
true="true"
|
||||
false="false"
|
||||
159
test.c
159
test.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2009 Dave Gamble
|
||||
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
|
||||
@@ -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,16 +46,16 @@ 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);
|
||||
|
||||
/* create buffer to succeed */
|
||||
/* the extra 64 bytes are in case a floating point value is printed */
|
||||
len = strlen(out) + 64;
|
||||
buf = malloc(len);
|
||||
/* the extra 5 bytes are because of inaccuracies when reserving memory */
|
||||
len = strlen(out) + 5;
|
||||
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();
|
||||
|
||||
206
test_utils.c
206
test_utils.c
@@ -1,206 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "cJSON_Utils.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* Some variables */
|
||||
char *temp = NULL;
|
||||
char *patchtext = NULL;
|
||||
char *patchedtext = NULL;
|
||||
|
||||
int i = 0;
|
||||
/* JSON Pointer tests: */
|
||||
cJSON *root = NULL;
|
||||
const char *json=
|
||||
"{"
|
||||
"\"foo\": [\"bar\", \"baz\"],"
|
||||
"\"\": 0,"
|
||||
"\"a/b\": 1,"
|
||||
"\"c%d\": 2,"
|
||||
"\"e^f\": 3,"
|
||||
"\"g|h\": 4,"
|
||||
"\"i\\\\j\": 5,"
|
||||
"\"k\\\"l\": 6,"
|
||||
"\" \": 7,"
|
||||
"\"m~n\": 8"
|
||||
"}";
|
||||
|
||||
const char *tests[12] = {"","/foo","/foo/0","/","/a~1b","/c%d","/e^f","/g|h","/i\\j","/k\"l","/ ","/m~0n"};
|
||||
|
||||
/* JSON Apply Patch tests: */
|
||||
const char *patches[15][3] =
|
||||
{
|
||||
{"{ \"foo\": \"bar\"}", "[{ \"op\": \"add\", \"path\": \"/baz\", \"value\": \"qux\" }]","{\"baz\": \"qux\",\"foo\": \"bar\"}"},
|
||||
{"{ \"foo\": [ \"bar\", \"baz\" ] }", "[{ \"op\": \"add\", \"path\": \"/foo/1\", \"value\": \"qux\" }]","{\"foo\": [ \"bar\", \"qux\", \"baz\" ] }"},
|
||||
{"{\"baz\": \"qux\",\"foo\": \"bar\"}"," [{ \"op\": \"remove\", \"path\": \"/baz\" }]","{\"foo\": \"bar\" }"},
|
||||
{"{ \"foo\": [ \"bar\", \"qux\", \"baz\" ] }","[{ \"op\": \"remove\", \"path\": \"/foo/1\" }]","{\"foo\": [ \"bar\", \"baz\" ] }"},
|
||||
{"{ \"baz\": \"qux\",\"foo\": \"bar\"}","[{ \"op\": \"replace\", \"path\": \"/baz\", \"value\": \"boo\" }]","{\"baz\": \"boo\",\"foo\": \"bar\"}"},
|
||||
{"{\"foo\": {\"bar\": \"baz\",\"waldo\": \"fred\"},\"qux\": {\"corge\": \"grault\"}}","[{ \"op\": \"move\", \"from\": \"/foo/waldo\", \"path\": \"/qux/thud\" }]","{\"foo\": {\"bar\": \"baz\"},\"qux\": {\"corge\": \"grault\",\"thud\": \"fred\"}}"},
|
||||
{"{ \"foo\": [ \"all\", \"grass\", \"cows\", \"eat\" ] }","[ { \"op\": \"move\", \"from\": \"/foo/1\", \"path\": \"/foo/3\" }]","{ \"foo\": [ \"all\", \"cows\", \"eat\", \"grass\" ] }"},
|
||||
{"{\"baz\": \"qux\",\"foo\": [ \"a\", 2, \"c\" ]}","[{ \"op\": \"test\", \"path\": \"/baz\", \"value\": \"qux\" },{ \"op\": \"test\", \"path\": \"/foo/1\", \"value\": 2 }]",""},
|
||||
{"{ \"baz\": \"qux\" }","[ { \"op\": \"test\", \"path\": \"/baz\", \"value\": \"bar\" }]",""},
|
||||
{"{ \"foo\": \"bar\" }","[{ \"op\": \"add\", \"path\": \"/child\", \"value\": { \"grandchild\": { } } }]","{\"foo\": \"bar\",\"child\": {\"grandchild\": {}}}"},
|
||||
{"{ \"foo\": \"bar\" }","[{ \"op\": \"add\", \"path\": \"/baz\", \"value\": \"qux\", \"xyz\": 123 }]","{\"foo\": \"bar\",\"baz\": \"qux\"}"},
|
||||
{"{ \"foo\": \"bar\" }","[{ \"op\": \"add\", \"path\": \"/baz/bat\", \"value\": \"qux\" }]",""},
|
||||
{"{\"/\": 9,\"~1\": 10}","[{\"op\": \"test\", \"path\": \"/~01\", \"value\": 10}]",""},
|
||||
{"{\"/\": 9,\"~1\": 10}","[{\"op\": \"test\", \"path\": \"/~01\", \"value\": \"10\"}]",""},
|
||||
{"{ \"foo\": [\"bar\"] }","[ { \"op\": \"add\", \"path\": \"/foo/-\", \"value\": [\"abc\", \"def\"] }]","{\"foo\": [\"bar\", [\"abc\", \"def\"]] }"}
|
||||
};
|
||||
|
||||
/* JSON Apply Merge tests: */
|
||||
const char *merges[15][3] =
|
||||
{
|
||||
{"{\"a\":\"b\"}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"},
|
||||
{"{\"a\":\"b\"}", "{\"b\":\"c\"}", "{\"a\":\"b\",\"b\":\"c\"}"},
|
||||
{"{\"a\":\"b\"}", "{\"a\":null}", "{}"},
|
||||
{"{\"a\":\"b\",\"b\":\"c\"}", "{\"a\":null}", "{\"b\":\"c\"}"},
|
||||
{"{\"a\":[\"b\"]}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"},
|
||||
{"{\"a\":\"c\"}", "{\"a\":[\"b\"]}", "{\"a\":[\"b\"]}"},
|
||||
{"{\"a\":{\"b\":\"c\"}}", "{\"a\":{\"b\":\"d\",\"c\":null}}", "{\"a\":{\"b\":\"d\"}}"},
|
||||
{"{\"a\":[{\"b\":\"c\"}]}", "{\"a\":[1]}", "{\"a\":[1]}"},
|
||||
{"[\"a\",\"b\"]", "[\"c\",\"d\"]", "[\"c\",\"d\"]"},
|
||||
{"{\"a\":\"b\"}", "[\"c\"]", "[\"c\"]"},
|
||||
{"{\"a\":\"foo\"}", "null", "null"},
|
||||
{"{\"a\":\"foo\"}", "\"bar\"", "\"bar\""},
|
||||
{"{\"e\":null}", "{\"a\":1}", "{\"e\":null,\"a\":1}"},
|
||||
{"[1,2]", "{\"a\":\"b\",\"c\":null}", "{\"a\":\"b\"}"},
|
||||
{"{}","{\"a\":{\"bb\":{\"ccc\":null}}}", "{\"a\":{\"bb\":{}}}"}
|
||||
};
|
||||
|
||||
|
||||
/* Misc tests */
|
||||
int numbers[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
const char *random = "QWERTYUIOPASDFGHJKLZXCVBNM";
|
||||
char buf[2] = {0,0};
|
||||
char *before = NULL;
|
||||
char *after = NULL;
|
||||
cJSON *object = NULL;
|
||||
cJSON *nums = NULL;
|
||||
cJSON *num6 = NULL;
|
||||
cJSON *sortme = NULL;
|
||||
|
||||
|
||||
printf("JSON Pointer Tests\n");
|
||||
root = cJSON_Parse(json);
|
||||
for (i = 0; i < 12; i++)
|
||||
{
|
||||
char *output = cJSON_Print(cJSONUtils_GetPointer(root, tests[i]));
|
||||
printf("Test %d:\n%s\n\n", i + 1, output);
|
||||
free(output);
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
|
||||
|
||||
printf("JSON Apply Patch Tests\n");
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
cJSON *object_to_be_patched = cJSON_Parse(patches[i][0]);
|
||||
cJSON *patch = cJSON_Parse(patches[i][1]);
|
||||
int err = cJSONUtils_ApplyPatches(object_to_be_patched, patch);
|
||||
char *output = cJSON_Print(object_to_be_patched);
|
||||
printf("Test %d (err %d):\n%s\n\n", i + 1, err, output);
|
||||
|
||||
free(output);
|
||||
cJSON_Delete(object_to_be_patched);
|
||||
cJSON_Delete(patch);
|
||||
}
|
||||
|
||||
/* JSON Generate Patch tests: */
|
||||
printf("JSON Generate Patch Tests\n");
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
cJSON *from;
|
||||
cJSON *to;
|
||||
cJSON *patch;
|
||||
char *out;
|
||||
if (!strlen(patches[i][2]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
from = cJSON_Parse(patches[i][0]);
|
||||
to = cJSON_Parse(patches[i][2]);
|
||||
patch = cJSONUtils_GeneratePatches(from, to);
|
||||
out = cJSON_Print(patch);
|
||||
printf("Test %d: (patch: %s):\n%s\n\n", i + 1, patches[i][1], out);
|
||||
|
||||
free(out);
|
||||
cJSON_Delete(from);
|
||||
cJSON_Delete(to);
|
||||
cJSON_Delete(patch);
|
||||
}
|
||||
|
||||
/* Misc tests: */
|
||||
printf("JSON Pointer construct\n");
|
||||
object = cJSON_CreateObject();
|
||||
nums = cJSON_CreateIntArray(numbers, 10);
|
||||
num6 = cJSON_GetArrayItem(nums, 6);
|
||||
cJSON_AddItemToObject(object, "numbers", nums);
|
||||
temp = cJSONUtils_FindPointerFromObjectTo(object, num6);
|
||||
printf("Pointer: [%s]\n", temp);
|
||||
free(temp);
|
||||
temp = cJSONUtils_FindPointerFromObjectTo(object, nums);
|
||||
printf("Pointer: [%s]\n", temp);
|
||||
free(temp);
|
||||
temp = cJSONUtils_FindPointerFromObjectTo(object, object);
|
||||
printf("Pointer: [%s]\n", temp);
|
||||
free(temp);
|
||||
cJSON_Delete(object);
|
||||
|
||||
/* JSON Sort test: */
|
||||
sortme = cJSON_CreateObject();
|
||||
for (i = 0; i < 26; i++)
|
||||
{
|
||||
buf[0] = random[i];
|
||||
cJSON_AddItemToObject(sortme, buf, cJSON_CreateNumber(1));
|
||||
}
|
||||
before = cJSON_PrintUnformatted(sortme);
|
||||
cJSONUtils_SortObject(sortme);
|
||||
after = cJSON_PrintUnformatted(sortme);
|
||||
printf("Before: [%s]\nAfter: [%s]\n\n", before, after);
|
||||
|
||||
free(before);
|
||||
free(after);
|
||||
cJSON_Delete(sortme);
|
||||
|
||||
/* Merge tests: */
|
||||
printf("JSON Merge Patch tests\n");
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
cJSON *object_to_be_merged = cJSON_Parse(merges[i][0]);
|
||||
cJSON *patch = cJSON_Parse(merges[i][1]);
|
||||
char *before_merge = cJSON_PrintUnformatted(object_to_be_merged);
|
||||
patchtext = cJSON_PrintUnformatted(patch);
|
||||
printf("Before: [%s] -> [%s] = ", before_merge, patchtext);
|
||||
object_to_be_merged = cJSONUtils_MergePatch(object_to_be_merged, patch);
|
||||
after = cJSON_PrintUnformatted(object_to_be_merged);
|
||||
printf("[%s] vs [%s] (%s)\n", after, merges[i][2], strcmp(after, merges[i][2]) ? "FAIL" : "OK");
|
||||
|
||||
free(before_merge);
|
||||
free(patchtext);
|
||||
free(after);
|
||||
cJSON_Delete(object_to_be_merged);
|
||||
cJSON_Delete(patch);
|
||||
}
|
||||
|
||||
/* Generate Merge tests: */
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
cJSON *from = cJSON_Parse(merges[i][0]);
|
||||
cJSON *to = cJSON_Parse(merges[i][2]);
|
||||
cJSON *patch = cJSONUtils_GenerateMergePatch(from,to);
|
||||
from = cJSONUtils_MergePatch(from,patch);
|
||||
patchtext = cJSON_PrintUnformatted(patch);
|
||||
patchedtext = cJSON_PrintUnformatted(from);
|
||||
printf("Patch [%s] vs [%s] = [%s] vs [%s] (%s)\n", patchtext, merges[i][1], patchedtext, merges[i][2], strcmp(patchedtext, merges[i][2]) ? "FAIL" : "OK");
|
||||
|
||||
cJSON_Delete(from);
|
||||
cJSON_Delete(to);
|
||||
cJSON_Delete(patch);
|
||||
free(patchtext);
|
||||
free(patchedtext);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
103
tests/CMakeLists.txt
Normal file
103
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,103 @@
|
||||
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
|
||||
parse_with_opts
|
||||
compare_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})
|
||||
|
||||
if (ENABLE_CJSON_UTILS)
|
||||
#copy test files
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/json-patch-tests")
|
||||
file(GLOB test_files "json-patch-tests/*")
|
||||
file(COPY ${test_files} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/json-patch-tests/")
|
||||
|
||||
set (cjson_utils_tests
|
||||
json_patch_tests
|
||||
old_utils_tests)
|
||||
|
||||
foreach (cjson_utils_test ${cjson_utils_tests})
|
||||
add_executable("${cjson_utils_test}" "${cjson_utils_test}.c")
|
||||
target_link_libraries("${cjson_utils_test}" "${CJSON_LIB}" "${CJSON_UTILS_LIB}" unity test-common)
|
||||
if(MEMORYCHECK_COMMAND)
|
||||
add_test(NAME "${cjson_utils_test}"
|
||||
COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} "${CMAKE_CURRENT_BINARY_DIR}/${cjson_utils_test}")
|
||||
else()
|
||||
add_test(NAME "${cjson_utils_test}"
|
||||
COMMAND "./${cjson_utils_test}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
add_dependencies(check ${cjson_utils_tests})
|
||||
endif()
|
||||
endif()
|
||||
97
tests/common.c
Normal file
97
tests/common.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
CJSON_PUBLIC(void) reset(cJSON *item)
|
||||
{
|
||||
if ((item != NULL) && (item->child != NULL))
|
||||
{
|
||||
cJSON_Delete(item->child);
|
||||
}
|
||||
if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference))
|
||||
{
|
||||
global_hooks.deallocate(item->valuestring);
|
||||
}
|
||||
if ((item->string != NULL) && !(item->type & cJSON_StringIsConst))
|
||||
{
|
||||
global_hooks.deallocate(item->string);
|
||||
}
|
||||
|
||||
memset(item, 0, sizeof(cJSON));
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(char*) read_file(const char *filename)
|
||||
{
|
||||
FILE *file = NULL;
|
||||
long length = 0;
|
||||
char *content = NULL;
|
||||
size_t read_chars = 0;
|
||||
|
||||
/* open in read binary mode */
|
||||
file = fopen(filename, "rb");
|
||||
if (file == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* get the length */
|
||||
if (fseek(file, 0, SEEK_END) != 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
length = ftell(file);
|
||||
if (length < 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
if (fseek(file, 0, SEEK_SET) != 0)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* allocate content buffer */
|
||||
content = (char*)malloc((size_t)length + sizeof(""));
|
||||
if (content == NULL)
|
||||
{
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* read the file into memory */
|
||||
read_chars = fread(content, sizeof(char), (size_t)length, file);
|
||||
if ((long)read_chars != length)
|
||||
{
|
||||
free(content);
|
||||
content = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
content[read_chars] = '\0';
|
||||
|
||||
|
||||
cleanup:
|
||||
if (file != NULL)
|
||||
{
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
51
tests/common.h
Normal file
51
tests/common.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CJSON_TESTS_COMMON_H
|
||||
#define CJSON_TESTS_COMMON_H
|
||||
|
||||
#include "../cJSON.c"
|
||||
|
||||
CJSON_PUBLIC(void) reset(cJSON *item);
|
||||
CJSON_PUBLIC(char*) read_file(const char *filename);
|
||||
|
||||
/* assertion helper macros */
|
||||
#define assert_has_type(item, item_type) TEST_ASSERT_BITS_MESSAGE(0xFF, item_type, item->type, "Item doesn't have expected type.")
|
||||
#define assert_has_no_reference(item) TEST_ASSERT_BITS_MESSAGE(cJSON_IsReference, 0, item->type, "Item should not have a string as reference.")
|
||||
#define assert_has_no_const_string(item) TEST_ASSERT_BITS_MESSAGE(cJSON_StringIsConst, 0, item->type, "Item should not have a const string.")
|
||||
#define assert_has_valuestring(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->valuestring, "Valuestring is NULL.")
|
||||
#define assert_has_no_valuestring(item) TEST_ASSERT_NULL_MESSAGE(item->valuestring, "Valuestring is not NULL.")
|
||||
#define assert_has_string(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->string, "String is NULL")
|
||||
#define assert_has_no_string(item) TEST_ASSERT_NULL_MESSAGE(item->string, "String is not NULL.")
|
||||
#define assert_not_in_list(item) \
|
||||
TEST_ASSERT_NULL_MESSAGE(item->next, "Linked list next pointer is not NULL.");\
|
||||
TEST_ASSERT_NULL_MESSAGE(item->prev, "Linked list previous pointer is not NULL.")
|
||||
#define assert_has_child(item) TEST_ASSERT_NOT_NULL_MESSAGE(item->child, "Item doesn't have a child.")
|
||||
#define assert_has_no_child(item) TEST_ASSERT_NULL_MESSAGE(item->child, "Item has a child.")
|
||||
#define assert_is_invalid(item) \
|
||||
assert_has_type(item, cJSON_Invalid);\
|
||||
assert_not_in_list(item);\
|
||||
assert_has_no_child(item);\
|
||||
assert_has_no_string(item);\
|
||||
assert_has_no_valuestring(item)
|
||||
|
||||
#endif
|
||||
192
tests/compare_tests.c
Normal file
192
tests/compare_tests.c
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
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 cJSON_bool compare_from_string(const char * const a, const char * const b, const cJSON_bool case_sensitive)
|
||||
{
|
||||
cJSON *a_json = NULL;
|
||||
cJSON *b_json = NULL;
|
||||
cJSON_bool result = false;
|
||||
|
||||
a_json = cJSON_Parse(a);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(a_json, "Failed to parse a.");
|
||||
b_json = cJSON_Parse(b);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(b_json, "Failed to parse b.");
|
||||
|
||||
result = cJSON_Compare(a_json, b_json, case_sensitive);
|
||||
|
||||
cJSON_Delete(a_json);
|
||||
cJSON_Delete(b_json);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_null_pointer_as_not_equal(void)
|
||||
{
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(NULL, NULL, true));
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(NULL, NULL, false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_invalid_as_not_equal(void)
|
||||
{
|
||||
cJSON invalid[1];
|
||||
memset(invalid, '\0', sizeof(invalid));
|
||||
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, false));
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, true));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_numbers(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("1", "1", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("1", "1", false));
|
||||
TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", false));
|
||||
|
||||
TEST_ASSERT_FALSE(compare_from_string("1", "2", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("1", "2", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_booleans(void)
|
||||
{
|
||||
/* true */
|
||||
TEST_ASSERT_TRUE(compare_from_string("true", "true", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("true", "true", false));
|
||||
|
||||
/* false */
|
||||
TEST_ASSERT_TRUE(compare_from_string("false", "false", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("false", "false", false));
|
||||
|
||||
/* mixed */
|
||||
TEST_ASSERT_FALSE(compare_from_string("true", "false", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("true", "false", false));
|
||||
TEST_ASSERT_FALSE(compare_from_string("false", "true", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("false", "true", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_null(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("null", "null", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("null", "null", false));
|
||||
|
||||
TEST_ASSERT_FALSE(compare_from_string("null", "true", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("null", "true", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_not_accept_invalid_types(void)
|
||||
{
|
||||
cJSON invalid[1];
|
||||
memset(invalid, '\0', sizeof(invalid));
|
||||
|
||||
invalid->type = cJSON_Number | cJSON_String;
|
||||
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, true));
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_strings(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("\"abcdefg\"", "\"abcdefg\"", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("\"abcdefg\"", "\"abcdefg\"", false));
|
||||
|
||||
TEST_ASSERT_FALSE(compare_from_string("\"ABCDEFG\"", "\"abcdefg\"", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("\"ABCDEFG\"", "\"abcdefg\"", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_raw(void)
|
||||
{
|
||||
cJSON *raw1 = NULL;
|
||||
cJSON *raw2 = NULL;
|
||||
|
||||
raw1 = cJSON_Parse("\"[true, false]\"");
|
||||
TEST_ASSERT_NOT_NULL(raw1);
|
||||
raw2 = cJSON_Parse("\"[true, false]\"");
|
||||
TEST_ASSERT_NOT_NULL(raw2);
|
||||
|
||||
raw1->type = cJSON_Raw;
|
||||
raw2->type = cJSON_Raw;
|
||||
|
||||
TEST_ASSERT_TRUE(cJSON_Compare(raw1, raw2, true));
|
||||
TEST_ASSERT_TRUE(cJSON_Compare(raw1, raw2, false));
|
||||
|
||||
cJSON_Delete(raw1);
|
||||
cJSON_Delete(raw2);
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_arrays(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("[]", "[]", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("[]", "[]", false));
|
||||
|
||||
TEST_ASSERT_TRUE(compare_from_string("[false,true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("[false,true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", false));
|
||||
|
||||
TEST_ASSERT_TRUE(compare_from_string("[[[1], 2]]", "[[[1], 2]]", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("[[[1], 2]]", "[[[1], 2]]", false));
|
||||
|
||||
TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_objects(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("{}", "{}", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("{}", "{}", false));
|
||||
|
||||
TEST_ASSERT_TRUE(compare_from_string(
|
||||
"{\"false\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
true));
|
||||
TEST_ASSERT_FALSE(compare_from_string(
|
||||
"{\"False\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
true));
|
||||
TEST_ASSERT_TRUE(compare_from_string(
|
||||
"{\"False\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
false));
|
||||
TEST_ASSERT_FALSE(compare_from_string(
|
||||
"{\"Flse\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
false));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(cjson_compare_should_compare_null_pointer_as_not_equal);
|
||||
RUN_TEST(cjson_compare_should_compare_invalid_as_not_equal);
|
||||
RUN_TEST(cjson_compare_should_compare_numbers);
|
||||
RUN_TEST(cjson_compare_should_compare_booleans);
|
||||
RUN_TEST(cjson_compare_should_compare_null);
|
||||
RUN_TEST(cjson_compare_should_not_accept_invalid_types);
|
||||
RUN_TEST(cjson_compare_should_compare_strings);
|
||||
RUN_TEST(cjson_compare_should_compare_raw);
|
||||
RUN_TEST(cjson_compare_should_compare_arrays);
|
||||
RUN_TEST(cjson_compare_should_compare_objects);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
22
tests/inputs/test1.expected
Normal file
22
tests/inputs/test1.expected
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"glossary": {
|
||||
"title": "example glossary",
|
||||
"GlossDiv": {
|
||||
"title": "S",
|
||||
"GlossList": {
|
||||
"GlossEntry": {
|
||||
"ID": "SGML",
|
||||
"SortAs": "SGML",
|
||||
"GlossTerm": "Standard Generalized Markup Language",
|
||||
"Acronym": "SGML",
|
||||
"Abbrev": "ISO 8879:1986",
|
||||
"GlossDef": {
|
||||
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||
"GlossSeeAlso": ["GML", "XML"]
|
||||
},
|
||||
"GlossSee": "markup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
tests/inputs/test10
Normal file
1
tests/inputs/test10
Normal file
@@ -0,0 +1 @@
|
||||
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
1
tests/inputs/test10.expected
Normal file
1
tests/inputs/test10.expected
Normal file
@@ -0,0 +1 @@
|
||||
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
8
tests/inputs/test11
Normal file
8
tests/inputs/test11
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "Jack (\"Bee\") Nimble",
|
||||
"format": {"type": "rect",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"interlace": false,"frame rate": 24
|
||||
}
|
||||
}
|
||||
10
tests/inputs/test11.expected
Normal file
10
tests/inputs/test11.expected
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Jack (\"Bee\") Nimble",
|
||||
"format": {
|
||||
"type": "rect",
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"interlace": false,
|
||||
"frame rate": 24
|
||||
}
|
||||
}
|
||||
18
tests/inputs/test2.expected
Normal file
18
tests/inputs/test2.expected
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"menu": {
|
||||
"id": "file",
|
||||
"value": "File",
|
||||
"popup": {
|
||||
"menuitem": [{
|
||||
"value": "New",
|
||||
"onclick": "CreateNewDoc()"
|
||||
}, {
|
||||
"value": "Open",
|
||||
"onclick": "OpenDoc()"
|
||||
}, {
|
||||
"value": "Close",
|
||||
"onclick": "CloseDoc()"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
28
tests/inputs/test3.expected
Normal file
28
tests/inputs/test3.expected
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"widget": {
|
||||
"debug": "on",
|
||||
"window": {
|
||||
"title": "Sample Konfabulator Widget",
|
||||
"name": "main_window",
|
||||
"width": 500,
|
||||
"height": 500
|
||||
},
|
||||
"image": {
|
||||
"src": "Images/Sun.png",
|
||||
"name": "sun1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 250,
|
||||
"alignment": "center"
|
||||
},
|
||||
"text": {
|
||||
"data": "Click Here",
|
||||
"size": 36,
|
||||
"style": "bold",
|
||||
"name": "text1",
|
||||
"hOffset": 250,
|
||||
"vOffset": 100,
|
||||
"alignment": "center",
|
||||
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
|
||||
}
|
||||
}
|
||||
}
|
||||
94
tests/inputs/test4.expected
Normal file
94
tests/inputs/test4.expected
Normal file
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"web-app": {
|
||||
"servlet": [{
|
||||
"servlet-name": "cofaxCDS",
|
||||
"servlet-class": "org.cofax.cds.CDSServlet",
|
||||
"init-param": {
|
||||
"configGlossary:installationAt": "Philadelphia, PA",
|
||||
"configGlossary:adminEmail": "ksm@pobox.com",
|
||||
"configGlossary:poweredBy": "Cofax",
|
||||
"configGlossary:poweredByIcon": "/images/cofax.gif",
|
||||
"configGlossary:staticPath": "/content/static",
|
||||
"templateProcessorClass": "org.cofax.WysiwygTemplate",
|
||||
"templateLoaderClass": "org.cofax.FilesTemplateLoader",
|
||||
"templatePath": "templates",
|
||||
"templateOverridePath": "",
|
||||
"defaultListTemplate": "listTemplate.htm",
|
||||
"defaultFileTemplate": "articleTemplate.htm",
|
||||
"useJSP": false,
|
||||
"jspListTemplate": "listTemplate.jsp",
|
||||
"jspFileTemplate": "articleTemplate.jsp",
|
||||
"cachePackageTagsTrack": 200,
|
||||
"cachePackageTagsStore": 200,
|
||||
"cachePackageTagsRefresh": 60,
|
||||
"cacheTemplatesTrack": 100,
|
||||
"cacheTemplatesStore": 50,
|
||||
"cacheTemplatesRefresh": 15,
|
||||
"cachePagesTrack": 200,
|
||||
"cachePagesStore": 100,
|
||||
"cachePagesRefresh": 10,
|
||||
"cachePagesDirtyRead": 10,
|
||||
"searchEngineListTemplate": "forSearchEnginesList.htm",
|
||||
"searchEngineFileTemplate": "forSearchEngines.htm",
|
||||
"searchEngineRobotsDb": "WEB-INF/robots.db",
|
||||
"useDataStore": true,
|
||||
"dataStoreClass": "org.cofax.SqlDataStore",
|
||||
"redirectionClass": "org.cofax.SqlRedirection",
|
||||
"dataStoreName": "cofax",
|
||||
"dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
|
||||
"dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
|
||||
"dataStoreUser": "sa",
|
||||
"dataStorePassword": "dataStoreTestQuery",
|
||||
"dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
|
||||
"dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
|
||||
"dataStoreInitConns": 10,
|
||||
"dataStoreMaxConns": 100,
|
||||
"dataStoreConnUsageLimit": 100,
|
||||
"dataStoreLogLevel": "debug",
|
||||
"maxUrlLength": 500
|
||||
}
|
||||
}, {
|
||||
"servlet-name": "cofaxEmail",
|
||||
"servlet-class": "org.cofax.cds.EmailServlet",
|
||||
"init-param": {
|
||||
"mailHost": "mail1",
|
||||
"mailHostOverride": "mail2"
|
||||
}
|
||||
}, {
|
||||
"servlet-name": "cofaxAdmin",
|
||||
"servlet-class": "org.cofax.cds.AdminServlet"
|
||||
}, {
|
||||
"servlet-name": "fileServlet",
|
||||
"servlet-class": "org.cofax.cds.FileServlet"
|
||||
}, {
|
||||
"servlet-name": "cofaxTools",
|
||||
"servlet-class": "org.cofax.cms.CofaxToolsServlet",
|
||||
"init-param": {
|
||||
"templatePath": "toolstemplates/",
|
||||
"log": 1,
|
||||
"logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
|
||||
"logMaxSize": "",
|
||||
"dataLog": 1,
|
||||
"dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
|
||||
"dataLogMaxSize": "",
|
||||
"removePageCache": "/content/admin/remove?cache=pages&id=",
|
||||
"removeTemplateCache": "/content/admin/remove?cache=templates&id=",
|
||||
"fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
|
||||
"lookInContext": 1,
|
||||
"adminGroupID": 4,
|
||||
"betaServer": true
|
||||
}
|
||||
}],
|
||||
"servlet-mapping": {
|
||||
"cofaxCDS": "/",
|
||||
"cofaxEmail": "/cofaxutil/aemail/*",
|
||||
"cofaxAdmin": "/admin/*",
|
||||
"fileServlet": "/static/*",
|
||||
"cofaxTools": "/tools/*"
|
||||
},
|
||||
"taglib": {
|
||||
"taglib-uri": "cofax.tld",
|
||||
"taglib-location": "/WEB-INF/tlds/cofax.tld"
|
||||
}
|
||||
}
|
||||
}
|
||||
54
tests/inputs/test5.expected
Normal file
54
tests/inputs/test5.expected
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"menu": {
|
||||
"header": "SVG Viewer",
|
||||
"items": [{
|
||||
"id": "Open"
|
||||
}, {
|
||||
"id": "OpenNew",
|
||||
"label": "Open New"
|
||||
}, null, {
|
||||
"id": "ZoomIn",
|
||||
"label": "Zoom In"
|
||||
}, {
|
||||
"id": "ZoomOut",
|
||||
"label": "Zoom Out"
|
||||
}, {
|
||||
"id": "OriginalView",
|
||||
"label": "Original View"
|
||||
}, null, {
|
||||
"id": "Quality"
|
||||
}, {
|
||||
"id": "Pause"
|
||||
}, {
|
||||
"id": "Mute"
|
||||
}, null, {
|
||||
"id": "Find",
|
||||
"label": "Find..."
|
||||
}, {
|
||||
"id": "FindAgain",
|
||||
"label": "Find Again"
|
||||
}, {
|
||||
"id": "Copy"
|
||||
}, {
|
||||
"id": "CopyAgain",
|
||||
"label": "Copy Again"
|
||||
}, {
|
||||
"id": "CopySVG",
|
||||
"label": "Copy SVG"
|
||||
}, {
|
||||
"id": "ViewSVG",
|
||||
"label": "View SVG"
|
||||
}, {
|
||||
"id": "ViewSource",
|
||||
"label": "View Source"
|
||||
}, {
|
||||
"id": "SaveAs",
|
||||
"label": "Save As"
|
||||
}, null, {
|
||||
"id": "Help"
|
||||
}, {
|
||||
"id": "About",
|
||||
"label": "About Adobe CVG Viewer..."
|
||||
}]
|
||||
}
|
||||
}
|
||||
22
tests/inputs/test7
Normal file
22
tests/inputs/test7
Normal file
@@ -0,0 +1,22 @@
|
||||
[
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
},
|
||||
{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.026020,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}
|
||||
]
|
||||
19
tests/inputs/test7.expected
Normal file
19
tests/inputs/test7.expected
Normal file
@@ -0,0 +1,19 @@
|
||||
[{
|
||||
"precision": "zip",
|
||||
"Latitude": 37.7668,
|
||||
"Longitude": -122.3959,
|
||||
"Address": "",
|
||||
"City": "SAN FRANCISCO",
|
||||
"State": "CA",
|
||||
"Zip": "94107",
|
||||
"Country": "US"
|
||||
}, {
|
||||
"precision": "zip",
|
||||
"Latitude": 37.371991,
|
||||
"Longitude": -122.02602,
|
||||
"Address": "",
|
||||
"City": "SUNNYVALE",
|
||||
"State": "CA",
|
||||
"Zip": "94085",
|
||||
"Country": "US"
|
||||
}]
|
||||
13
tests/inputs/test8
Normal file
13
tests/inputs/test8
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http:/*www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
}
|
||||
}
|
||||
13
tests/inputs/test8.expected
Normal file
13
tests/inputs/test8.expected
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"Image": {
|
||||
"Width": 800,
|
||||
"Height": 600,
|
||||
"Title": "View from 15th Floor",
|
||||
"Thumbnail": {
|
||||
"Url": "http:/*www.example.com/image/481989943",
|
||||
"Height": 125,
|
||||
"Width": "100"
|
||||
},
|
||||
"IDs": [116, 943, 234, 38793]
|
||||
}
|
||||
}
|
||||
5
tests/inputs/test9
Normal file
5
tests/inputs/test9
Normal file
@@ -0,0 +1,5 @@
|
||||
[
|
||||
[0, -1, 0],
|
||||
[1, 0, 0],
|
||||
[0, 0, 1]
|
||||
]
|
||||
1
tests/inputs/test9.expected
Normal file
1
tests/inputs/test9.expected
Normal file
@@ -0,0 +1 @@
|
||||
[[0, -1, 0], [1, 0, 0], [0, 0, 1]]
|
||||
10
tests/json-patch-tests/.editorconfig
Normal file
10
tests/json-patch-tests/.editorconfig
Normal file
@@ -0,0 +1,10 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
4
tests/json-patch-tests/.gitignore
vendored
Normal file
4
tests/json-patch-tests/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*~
|
||||
\#*
|
||||
!.editorconfig
|
||||
!.gitignore
|
||||
2
tests/json-patch-tests/.npmignore
Normal file
2
tests/json-patch-tests/.npmignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.editorconfig
|
||||
.gitignore
|
||||
75
tests/json-patch-tests/README.md
Normal file
75
tests/json-patch-tests/README.md
Normal file
@@ -0,0 +1,75 @@
|
||||
JSON Patch Tests
|
||||
================
|
||||
|
||||
These are test cases for implementations of [IETF JSON Patch (RFC6902)](http://tools.ietf.org/html/rfc6902).
|
||||
|
||||
Some implementations can be found at [jsonpatch.com](http://jsonpatch.com).
|
||||
|
||||
|
||||
Test Format
|
||||
-----------
|
||||
|
||||
Each test file is a JSON document that contains an array of test records. A
|
||||
test record is an object with the following members:
|
||||
|
||||
- doc: The JSON document to test against
|
||||
- patch: The patch(es) to apply
|
||||
- expected: The expected resulting document, OR
|
||||
- error: A string describing an expected error
|
||||
- comment: A string describing the test
|
||||
- disabled: True if the test should be skipped
|
||||
|
||||
All fields except 'doc' and 'patch' are optional. Test records consisting only
|
||||
of a comment are also OK.
|
||||
|
||||
|
||||
Files
|
||||
-----
|
||||
|
||||
- tests.json: the main test file
|
||||
- spec_tests.json: tests from the RFC6902 spec
|
||||
|
||||
|
||||
Writing Tests
|
||||
-------------
|
||||
|
||||
All tests should have a descriptive comment. Tests should be as
|
||||
simple as possible - just what's required to test a specific piece of
|
||||
behavior. If you want to test interacting behaviors, create tests for
|
||||
each behavior as well as the interaction.
|
||||
|
||||
If an 'error' member is specified, the error text should describe the
|
||||
error the implementation should raise - *not* what's being tested.
|
||||
Implementation error strings will vary, but the suggested error should
|
||||
be easily matched to the implementation error string. Try to avoid
|
||||
creating error tests that might pass because an incorrect error was
|
||||
reported.
|
||||
|
||||
Please feel free to contribute!
|
||||
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
The seed test set was adapted from Byron Ruth's
|
||||
[jsonpatch-js](https://github.com/bruth/jsonpatch-js/blob/master/test.js) and
|
||||
extended by [Mike McCabe](https://github.com/mikemccabe).
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2014 The Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
84
tests/json-patch-tests/cjson-utils-tests.json
Normal file
84
tests/json-patch-tests/cjson-utils-tests.json
Normal file
@@ -0,0 +1,84 @@
|
||||
[
|
||||
{
|
||||
"comment": "1",
|
||||
"doc": { "foo": "bar"},
|
||||
"patch": [{ "op": "add", "path": "/baz", "value": "qux" }],
|
||||
"expected": {"baz": "qux", "foo": "bar"}
|
||||
},
|
||||
{
|
||||
"comment": "2",
|
||||
"doc": { "foo": [ "bar", "baz" ] },
|
||||
"patch": [{ "op": "add", "path": "/foo/1", "value": "qux" }],
|
||||
"expected": {"foo": [ "bar", "qux", "baz" ] }
|
||||
},
|
||||
{
|
||||
"comment": "3",
|
||||
"doc": {"baz": "qux","foo": "bar"},
|
||||
"patch": [{ "op": "remove", "path": "/baz" }],
|
||||
"expected": {"foo": "bar" }
|
||||
},
|
||||
{
|
||||
"comment": "4",
|
||||
"doc": { "foo": [ "bar", "qux", "baz" ] },
|
||||
"patch": [{ "op": "remove", "path": "/foo/1" }],
|
||||
"expected": {"foo": [ "bar", "baz" ] }
|
||||
},
|
||||
{
|
||||
"comment": "5",
|
||||
"doc": { "baz": "qux","foo": "bar"},
|
||||
"patch": [{ "op": "replace", "path": "/baz", "value": "boo" }],
|
||||
"expected": {"baz": "boo","foo": "bar"}
|
||||
},
|
||||
{
|
||||
"comment": "6",
|
||||
"doc": {"foo": {"bar": "baz","waldo": "fred"},"qux": {"corge": "grault"}},
|
||||
"patch": [{ "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }],
|
||||
"expected": {"foo": {"bar": "baz"},"qux": {"corge": "grault","thud": "fred"}}
|
||||
},
|
||||
{
|
||||
"comment": "7",
|
||||
"doc": { "foo": [ "all", "grass", "cows", "eat" ] },
|
||||
"patch": [ { "op": "move", "from": "/foo/1", "path": "/foo/3" }],
|
||||
"expected": { "foo": [ "all", "cows", "eat", "grass" ] }
|
||||
},
|
||||
{
|
||||
"comment": "8",
|
||||
"doc": {"baz": "qux","foo": [ "a", 2, "c" ]},
|
||||
"patch": [{ "op": "test", "path": "/baz", "value": "qux" },{ "op": "test", "path": "/foo/1", "value": 2 }]
|
||||
},
|
||||
{
|
||||
"comment": "9",
|
||||
"doc": { "baz": "qux" },
|
||||
"patch": [ { "op": "test", "path": "/baz", "value": "bar" }],
|
||||
"error": "\"bar\" doesn't exist"
|
||||
},
|
||||
{
|
||||
"comment": "10",
|
||||
"doc": { "foo": "bar" },
|
||||
"patch": [{ "op": "add", "path": "/child", "value": { "grandchild": { } } }],
|
||||
"expected": {"foo": "bar","child": {"grandchild": {}}}
|
||||
},
|
||||
{
|
||||
"comment": "11",
|
||||
"doc": { "foo": "bar" },
|
||||
"patch": [{ "op": "add", "path": "/baz", "value": "qux", "xyz": 123 }],
|
||||
"expected": {"foo": "bar","baz": "qux"}
|
||||
},
|
||||
{
|
||||
"comment": "12",
|
||||
"doc": { "foo": "bar" },
|
||||
"patch": [{ "op": "add", "path": "/baz/bat", "value": "qux" }],
|
||||
"error": "Can't add to nonexistent object"
|
||||
},
|
||||
{
|
||||
"comment": "13",
|
||||
"doc": {"/": 9,"~1": 10},
|
||||
"patch": [{"op": "test", "path": "/~01", "value": 10}]
|
||||
},
|
||||
{
|
||||
"comment": "14",
|
||||
"doc": { "foo": ["bar"] },
|
||||
"patch": [ { "op": "add", "path": "/foo/-", "value": ["abc", "def"] }],
|
||||
"expected": {"foo": ["bar", ["abc", "def"]] }
|
||||
}
|
||||
]
|
||||
15
tests/json-patch-tests/package.json
Normal file
15
tests/json-patch-tests/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "json-patch-test-suite",
|
||||
"version": "1.1.0",
|
||||
"description": "JSON Patch RFC 6902 test suite",
|
||||
"repository": "github:json-patch/json-patch-tests",
|
||||
"homepage": "https://github.com/json-patch/json-patch-tests",
|
||||
"bugs": "https://github.com/json-patch/json-patch-tests/issues",
|
||||
"keywords": [
|
||||
"JSON",
|
||||
"Patch",
|
||||
"test",
|
||||
"suite"
|
||||
],
|
||||
"license": "Apache-2.0"
|
||||
}
|
||||
233
tests/json-patch-tests/spec_tests.json
Normal file
233
tests/json-patch-tests/spec_tests.json
Normal file
@@ -0,0 +1,233 @@
|
||||
[
|
||||
{
|
||||
"comment": "4.1. add with missing object",
|
||||
"doc": { "q": { "bar": 2 } },
|
||||
"patch": [ {"op": "add", "path": "/a/b", "value": 1} ],
|
||||
"error":
|
||||
"path /a does not exist -- missing objects are not created recursively"
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.1. Adding an Object Member",
|
||||
"doc": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "add", "path": "/baz", "value": "qux" }
|
||||
],
|
||||
"expected": {
|
||||
"baz": "qux",
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.2. Adding an Array Element",
|
||||
"doc": {
|
||||
"foo": [ "bar", "baz" ]
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "add", "path": "/foo/1", "value": "qux" }
|
||||
],
|
||||
"expected": {
|
||||
"foo": [ "bar", "qux", "baz" ]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.3. Removing an Object Member",
|
||||
"doc": {
|
||||
"baz": "qux",
|
||||
"foo": "bar"
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "remove", "path": "/baz" }
|
||||
],
|
||||
"expected": {
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.4. Removing an Array Element",
|
||||
"doc": {
|
||||
"foo": [ "bar", "qux", "baz" ]
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "remove", "path": "/foo/1" }
|
||||
],
|
||||
"expected": {
|
||||
"foo": [ "bar", "baz" ]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.5. Replacing a Value",
|
||||
"doc": {
|
||||
"baz": "qux",
|
||||
"foo": "bar"
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "replace", "path": "/baz", "value": "boo" }
|
||||
],
|
||||
"expected": {
|
||||
"baz": "boo",
|
||||
"foo": "bar"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.6. Moving a Value",
|
||||
"doc": {
|
||||
"foo": {
|
||||
"bar": "baz",
|
||||
"waldo": "fred"
|
||||
},
|
||||
"qux": {
|
||||
"corge": "grault"
|
||||
}
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }
|
||||
],
|
||||
"expected": {
|
||||
"foo": {
|
||||
"bar": "baz"
|
||||
},
|
||||
"qux": {
|
||||
"corge": "grault",
|
||||
"thud": "fred"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.7. Moving an Array Element",
|
||||
"doc": {
|
||||
"foo": [ "all", "grass", "cows", "eat" ]
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "move", "from": "/foo/1", "path": "/foo/3" }
|
||||
],
|
||||
"expected": {
|
||||
"foo": [ "all", "cows", "eat", "grass" ]
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.8. Testing a Value: Success",
|
||||
"doc": {
|
||||
"baz": "qux",
|
||||
"foo": [ "a", 2, "c" ]
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "test", "path": "/baz", "value": "qux" },
|
||||
{ "op": "test", "path": "/foo/1", "value": 2 }
|
||||
],
|
||||
"expected": {
|
||||
"baz": "qux",
|
||||
"foo": [ "a", 2, "c" ]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.9. Testing a Value: Error",
|
||||
"doc": {
|
||||
"baz": "qux"
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "test", "path": "/baz", "value": "bar" }
|
||||
],
|
||||
"error": "string not equivalent"
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.10. Adding a nested Member Object",
|
||||
"doc": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "add", "path": "/child", "value": { "grandchild": { } } }
|
||||
],
|
||||
"expected": {
|
||||
"foo": "bar",
|
||||
"child": {
|
||||
"grandchild": {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.11. Ignoring Unrecognized Elements",
|
||||
"doc": {
|
||||
"foo":"bar"
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "add", "path": "/baz", "value": "qux", "xyz": 123 }
|
||||
],
|
||||
"expected": {
|
||||
"foo":"bar",
|
||||
"baz":"qux"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.12. Adding to a Non-existent Target",
|
||||
"doc": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "add", "path": "/baz/bat", "value": "qux" }
|
||||
],
|
||||
"error": "add to a non-existent target"
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.13 Invalid JSON Patch Document",
|
||||
"doc": {
|
||||
"foo": "bar"
|
||||
},
|
||||
"patch": [
|
||||
{ "op": "add", "path": "/baz", "value": "qux", "op": "remove" }
|
||||
],
|
||||
"error": "operation has two 'op' members",
|
||||
"disabled": true
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.14. ~ Escape Ordering",
|
||||
"doc": {
|
||||
"/": 9,
|
||||
"~1": 10
|
||||
},
|
||||
"patch": [{"op": "test", "path": "/~01", "value": 10}],
|
||||
"expected": {
|
||||
"/": 9,
|
||||
"~1": 10
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.15. Comparing Strings and Numbers",
|
||||
"doc": {
|
||||
"/": 9,
|
||||
"~1": 10
|
||||
},
|
||||
"patch": [{"op": "test", "path": "/~01", "value": "10"}],
|
||||
"error": "number is not equal to string"
|
||||
},
|
||||
|
||||
{
|
||||
"comment": "A.16. Adding an Array Value",
|
||||
"doc": {
|
||||
"foo": ["bar"]
|
||||
},
|
||||
"patch": [{ "op": "add", "path": "/foo/-", "value": ["abc", "def"] }],
|
||||
"expected": {
|
||||
"foo": ["bar", ["abc", "def"]]
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
429
tests/json-patch-tests/tests.json
Normal file
429
tests/json-patch-tests/tests.json
Normal file
@@ -0,0 +1,429 @@
|
||||
[
|
||||
{ "comment": "empty list, empty docs",
|
||||
"doc": {},
|
||||
"patch": [],
|
||||
"expected": {} },
|
||||
|
||||
{ "comment": "empty patch list",
|
||||
"doc": {"foo": 1},
|
||||
"patch": [],
|
||||
"expected": {"foo": 1} },
|
||||
|
||||
{ "comment": "rearrangements OK?",
|
||||
"doc": {"foo": 1, "bar": 2},
|
||||
"patch": [],
|
||||
"expected": {"bar":2, "foo": 1} },
|
||||
|
||||
{ "comment": "rearrangements OK? How about one level down ... array",
|
||||
"doc": [{"foo": 1, "bar": 2}],
|
||||
"patch": [],
|
||||
"expected": [{"bar":2, "foo": 1}] },
|
||||
|
||||
{ "comment": "rearrangements OK? How about one level down...",
|
||||
"doc": {"foo":{"foo": 1, "bar": 2}},
|
||||
"patch": [],
|
||||
"expected": {"foo":{"bar":2, "foo": 1}} },
|
||||
|
||||
{ "comment": "add replaces any existing field",
|
||||
"doc": {"foo": null},
|
||||
"patch": [{"op": "add", "path": "/foo", "value":1}],
|
||||
"expected": {"foo": 1} },
|
||||
|
||||
{ "comment": "toplevel array",
|
||||
"doc": [],
|
||||
"patch": [{"op": "add", "path": "/0", "value": "foo"}],
|
||||
"expected": ["foo"] },
|
||||
|
||||
{ "comment": "toplevel array, no change",
|
||||
"doc": ["foo"],
|
||||
"patch": [],
|
||||
"expected": ["foo"] },
|
||||
|
||||
{ "comment": "toplevel object, numeric string",
|
||||
"doc": {},
|
||||
"patch": [{"op": "add", "path": "/foo", "value": "1"}],
|
||||
"expected": {"foo":"1"} },
|
||||
|
||||
{ "comment": "toplevel object, integer",
|
||||
"doc": {},
|
||||
"patch": [{"op": "add", "path": "/foo", "value": 1}],
|
||||
"expected": {"foo":1} },
|
||||
|
||||
{ "comment": "Toplevel scalar values OK?",
|
||||
"doc": "foo",
|
||||
"patch": [{"op": "replace", "path": "", "value": "bar"}],
|
||||
"expected": "bar",
|
||||
"disabled": true },
|
||||
|
||||
{ "comment": "replace object document with array document?",
|
||||
"doc": {},
|
||||
"patch": [{"op": "add", "path": "", "value": []}],
|
||||
"expected": [] },
|
||||
|
||||
{ "comment": "replace array document with object document?",
|
||||
"doc": [],
|
||||
"patch": [{"op": "add", "path": "", "value": {}}],
|
||||
"expected": {} },
|
||||
|
||||
{ "comment": "append to root array document?",
|
||||
"doc": [],
|
||||
"patch": [{"op": "add", "path": "/-", "value": "hi"}],
|
||||
"expected": ["hi"] },
|
||||
|
||||
{ "comment": "Add, / target",
|
||||
"doc": {},
|
||||
"patch": [ {"op": "add", "path": "/", "value":1 } ],
|
||||
"expected": {"":1} },
|
||||
|
||||
{ "comment": "Add, /foo/ deep target (trailing slash)",
|
||||
"doc": {"foo": {}},
|
||||
"patch": [ {"op": "add", "path": "/foo/", "value":1 } ],
|
||||
"expected": {"foo":{"": 1}} },
|
||||
|
||||
{ "comment": "Add composite value at top level",
|
||||
"doc": {"foo": 1},
|
||||
"patch": [{"op": "add", "path": "/bar", "value": [1, 2]}],
|
||||
"expected": {"foo": 1, "bar": [1, 2]} },
|
||||
|
||||
{ "comment": "Add into composite value",
|
||||
"doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||
"patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}],
|
||||
"expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]} },
|
||||
|
||||
{ "doc": {"bar": [1, 2]},
|
||||
"patch": [{"op": "add", "path": "/bar/8", "value": "5"}],
|
||||
"error": "Out of bounds (upper)" },
|
||||
|
||||
{ "doc": {"bar": [1, 2]},
|
||||
"patch": [{"op": "add", "path": "/bar/-1", "value": "5"}],
|
||||
"error": "Out of bounds (lower)" },
|
||||
|
||||
{ "doc": {"foo": 1},
|
||||
"patch": [{"op": "add", "path": "/bar", "value": true}],
|
||||
"expected": {"foo": 1, "bar": true} },
|
||||
|
||||
{ "doc": {"foo": 1},
|
||||
"patch": [{"op": "add", "path": "/bar", "value": false}],
|
||||
"expected": {"foo": 1, "bar": false} },
|
||||
|
||||
{ "doc": {"foo": 1},
|
||||
"patch": [{"op": "add", "path": "/bar", "value": null}],
|
||||
"expected": {"foo": 1, "bar": null} },
|
||||
|
||||
{ "comment": "0 can be an array index or object element name",
|
||||
"doc": {"foo": 1},
|
||||
"patch": [{"op": "add", "path": "/0", "value": "bar"}],
|
||||
"expected": {"foo": 1, "0": "bar" } },
|
||||
|
||||
{ "doc": ["foo"],
|
||||
"patch": [{"op": "add", "path": "/1", "value": "bar"}],
|
||||
"expected": ["foo", "bar"] },
|
||||
|
||||
{ "doc": ["foo", "sil"],
|
||||
"patch": [{"op": "add", "path": "/1", "value": "bar"}],
|
||||
"expected": ["foo", "bar", "sil"] },
|
||||
|
||||
{ "doc": ["foo", "sil"],
|
||||
"patch": [{"op": "add", "path": "/0", "value": "bar"}],
|
||||
"expected": ["bar", "foo", "sil"] },
|
||||
|
||||
{ "comment": "push item to array via last index + 1",
|
||||
"doc": ["foo", "sil"],
|
||||
"patch": [{"op":"add", "path": "/2", "value": "bar"}],
|
||||
"expected": ["foo", "sil", "bar"] },
|
||||
|
||||
{ "comment": "add item to array at index > length should fail",
|
||||
"doc": ["foo", "sil"],
|
||||
"patch": [{"op":"add", "path": "/3", "value": "bar"}],
|
||||
"error": "index is greater than number of items in array" },
|
||||
|
||||
{ "comment": "test against implementation-specific numeric parsing",
|
||||
"doc": {"1e0": "foo"},
|
||||
"patch": [{"op": "test", "path": "/1e0", "value": "foo"}],
|
||||
"expected": {"1e0": "foo"} },
|
||||
|
||||
{ "comment": "test with bad number should fail",
|
||||
"doc": ["foo", "bar"],
|
||||
"patch": [{"op": "test", "path": "/1e0", "value": "bar"}],
|
||||
"error": "test op shouldn't get array element 1" },
|
||||
|
||||
{ "doc": ["foo", "sil"],
|
||||
"patch": [{"op": "add", "path": "/bar", "value": 42}],
|
||||
"error": "Object operation on array target" },
|
||||
|
||||
{ "doc": ["foo", "sil"],
|
||||
"patch": [{"op": "add", "path": "/1", "value": ["bar", "baz"]}],
|
||||
"expected": ["foo", ["bar", "baz"], "sil"],
|
||||
"comment": "value in array add not flattened" },
|
||||
|
||||
{ "doc": {"foo": 1, "bar": [1, 2, 3, 4]},
|
||||
"patch": [{"op": "remove", "path": "/bar"}],
|
||||
"expected": {"foo": 1} },
|
||||
|
||||
{ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||
"patch": [{"op": "remove", "path": "/baz/0/qux"}],
|
||||
"expected": {"foo": 1, "baz": [{}]} },
|
||||
|
||||
{ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||
"patch": [{"op": "replace", "path": "/foo", "value": [1, 2, 3, 4]}],
|
||||
"expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]} },
|
||||
|
||||
{ "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]},
|
||||
"patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}],
|
||||
"expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]} },
|
||||
|
||||
{ "doc": ["foo"],
|
||||
"patch": [{"op": "replace", "path": "/0", "value": "bar"}],
|
||||
"expected": ["bar"] },
|
||||
|
||||
{ "doc": [""],
|
||||
"patch": [{"op": "replace", "path": "/0", "value": 0}],
|
||||
"expected": [0] },
|
||||
|
||||
{ "doc": [""],
|
||||
"patch": [{"op": "replace", "path": "/0", "value": true}],
|
||||
"expected": [true] },
|
||||
|
||||
{ "doc": [""],
|
||||
"patch": [{"op": "replace", "path": "/0", "value": false}],
|
||||
"expected": [false] },
|
||||
|
||||
{ "doc": [""],
|
||||
"patch": [{"op": "replace", "path": "/0", "value": null}],
|
||||
"expected": [null] },
|
||||
|
||||
{ "doc": ["foo", "sil"],
|
||||
"patch": [{"op": "replace", "path": "/1", "value": ["bar", "baz"]}],
|
||||
"expected": ["foo", ["bar", "baz"]],
|
||||
"comment": "value in array replace not flattened" },
|
||||
|
||||
{ "comment": "replace whole document",
|
||||
"doc": {"foo": "bar"},
|
||||
"patch": [{"op": "replace", "path": "", "value": {"baz": "qux"}}],
|
||||
"expected": {"baz": "qux"} },
|
||||
|
||||
{ "comment": "spurious patch properties",
|
||||
"doc": {"foo": 1},
|
||||
"patch": [{"op": "test", "path": "/foo", "value": 1, "spurious": 1}],
|
||||
"expected": {"foo": 1} },
|
||||
|
||||
{ "doc": {"foo": null},
|
||||
"patch": [{"op": "test", "path": "/foo", "value": null}],
|
||||
"comment": "null value should be valid obj property" },
|
||||
|
||||
{ "doc": {"foo": null},
|
||||
"patch": [{"op": "replace", "path": "/foo", "value": "truthy"}],
|
||||
"expected": {"foo": "truthy"},
|
||||
"comment": "null value should be valid obj property to be replaced with something truthy" },
|
||||
|
||||
{ "doc": {"foo": null},
|
||||
"patch": [{"op": "move", "from": "/foo", "path": "/bar"}],
|
||||
"expected": {"bar": null},
|
||||
"comment": "null value should be valid obj property to be moved" },
|
||||
|
||||
{ "doc": {"foo": null},
|
||||
"patch": [{"op": "copy", "from": "/foo", "path": "/bar"}],
|
||||
"expected": {"foo": null, "bar": null},
|
||||
"comment": "null value should be valid obj property to be copied" },
|
||||
|
||||
{ "doc": {"foo": null},
|
||||
"patch": [{"op": "remove", "path": "/foo"}],
|
||||
"expected": {},
|
||||
"comment": "null value should be valid obj property to be removed" },
|
||||
|
||||
{ "doc": {"foo": "bar"},
|
||||
"patch": [{"op": "replace", "path": "/foo", "value": null}],
|
||||
"expected": {"foo": null},
|
||||
"comment": "null value should still be valid obj property replace other value" },
|
||||
|
||||
{ "doc": {"foo": {"foo": 1, "bar": 2}},
|
||||
"patch": [{"op": "test", "path": "/foo", "value": {"bar": 2, "foo": 1}}],
|
||||
"comment": "test should pass despite rearrangement" },
|
||||
|
||||
{ "doc": {"foo": [{"foo": 1, "bar": 2}]},
|
||||
"patch": [{"op": "test", "path": "/foo", "value": [{"bar": 2, "foo": 1}]}],
|
||||
"comment": "test should pass despite (nested) rearrangement" },
|
||||
|
||||
{ "doc": {"foo": {"bar": [1, 2, 5, 4]}},
|
||||
"patch": [{"op": "test", "path": "/foo", "value": {"bar": [1, 2, 5, 4]}}],
|
||||
"comment": "test should pass - no error" },
|
||||
|
||||
{ "doc": {"foo": {"bar": [1, 2, 5, 4]}},
|
||||
"patch": [{"op": "test", "path": "/foo", "value": [1, 2]}],
|
||||
"error": "test op should fail" },
|
||||
|
||||
{ "comment": "Whole document",
|
||||
"doc": { "foo": 1 },
|
||||
"patch": [{"op": "test", "path": "", "value": {"foo": 1}}],
|
||||
"disabled": true },
|
||||
|
||||
{ "comment": "Empty-string element",
|
||||
"doc": { "": 1 },
|
||||
"patch": [{"op": "test", "path": "/", "value": 1}] },
|
||||
|
||||
{ "doc": {
|
||||
"foo": ["bar", "baz"],
|
||||
"": 0,
|
||||
"a/b": 1,
|
||||
"c%d": 2,
|
||||
"e^f": 3,
|
||||
"g|h": 4,
|
||||
"i\\j": 5,
|
||||
"k\"l": 6,
|
||||
" ": 7,
|
||||
"m~n": 8
|
||||
},
|
||||
"patch": [{"op": "test", "path": "/foo", "value": ["bar", "baz"]},
|
||||
{"op": "test", "path": "/foo/0", "value": "bar"},
|
||||
{"op": "test", "path": "/", "value": 0},
|
||||
{"op": "test", "path": "/a~1b", "value": 1},
|
||||
{"op": "test", "path": "/c%d", "value": 2},
|
||||
{"op": "test", "path": "/e^f", "value": 3},
|
||||
{"op": "test", "path": "/g|h", "value": 4},
|
||||
{"op": "test", "path": "/i\\j", "value": 5},
|
||||
{"op": "test", "path": "/k\"l", "value": 6},
|
||||
{"op": "test", "path": "/ ", "value": 7},
|
||||
{"op": "test", "path": "/m~0n", "value": 8}] },
|
||||
|
||||
{ "comment": "Move to same location has no effect",
|
||||
"doc": {"foo": 1},
|
||||
"patch": [{"op": "move", "from": "/foo", "path": "/foo"}],
|
||||
"expected": {"foo": 1} },
|
||||
|
||||
{ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||
"patch": [{"op": "move", "from": "/foo", "path": "/bar"}],
|
||||
"expected": {"baz": [{"qux": "hello"}], "bar": 1} },
|
||||
|
||||
{ "doc": {"baz": [{"qux": "hello"}], "bar": 1},
|
||||
"patch": [{"op": "move", "from": "/baz/0/qux", "path": "/baz/1"}],
|
||||
"expected": {"baz": [{}, "hello"], "bar": 1} },
|
||||
|
||||
{ "doc": {"baz": [{"qux": "hello"}], "bar": 1},
|
||||
"patch": [{"op": "copy", "from": "/baz/0", "path": "/boo"}],
|
||||
"expected": {"baz":[{"qux":"hello"}],"bar":1,"boo":{"qux":"hello"}} },
|
||||
|
||||
{ "comment": "replacing the root of the document is possible with add",
|
||||
"doc": {"foo": "bar"},
|
||||
"patch": [{"op": "add", "path": "", "value": {"baz": "qux"}}],
|
||||
"expected": {"baz":"qux"}},
|
||||
|
||||
{ "comment": "Adding to \"/-\" adds to the end of the array",
|
||||
"doc": [ 1, 2 ],
|
||||
"patch": [ { "op": "add", "path": "/-", "value": { "foo": [ "bar", "baz" ] } } ],
|
||||
"expected": [ 1, 2, { "foo": [ "bar", "baz" ] } ]},
|
||||
|
||||
{ "comment": "Adding to \"/-\" adds to the end of the array, even n levels down",
|
||||
"doc": [ 1, 2, [ 3, [ 4, 5 ] ] ],
|
||||
"patch": [ { "op": "add", "path": "/2/1/-", "value": { "foo": [ "bar", "baz" ] } } ],
|
||||
"expected": [ 1, 2, [ 3, [ 4, 5, { "foo": [ "bar", "baz" ] } ] ] ]},
|
||||
|
||||
{ "comment": "test remove with bad number should fail",
|
||||
"doc": {"foo": 1, "baz": [{"qux": "hello"}]},
|
||||
"patch": [{"op": "remove", "path": "/baz/1e0/qux"}],
|
||||
"error": "remove op shouldn't remove from array with bad number" },
|
||||
|
||||
{ "comment": "test remove on array",
|
||||
"doc": [1, 2, 3, 4],
|
||||
"patch": [{"op": "remove", "path": "/0"}],
|
||||
"expected": [2, 3, 4] },
|
||||
|
||||
{ "comment": "test repeated removes",
|
||||
"doc": [1, 2, 3, 4],
|
||||
"patch": [{ "op": "remove", "path": "/1" },
|
||||
{ "op": "remove", "path": "/2" }],
|
||||
"expected": [1, 3] },
|
||||
|
||||
{ "comment": "test remove with bad index should fail",
|
||||
"doc": [1, 2, 3, 4],
|
||||
"patch": [{"op": "remove", "path": "/1e0"}],
|
||||
"error": "remove op shouldn't remove from array with bad number" },
|
||||
|
||||
{ "comment": "test replace with bad number should fail",
|
||||
"doc": [""],
|
||||
"patch": [{"op": "replace", "path": "/1e0", "value": false}],
|
||||
"error": "replace op shouldn't replace in array with bad number" },
|
||||
|
||||
{ "comment": "test copy with bad number should fail",
|
||||
"doc": {"baz": [1,2,3], "bar": 1},
|
||||
"patch": [{"op": "copy", "from": "/baz/1e0", "path": "/boo"}],
|
||||
"error": "copy op shouldn't work with bad number" },
|
||||
|
||||
{ "comment": "test move with bad number should fail",
|
||||
"doc": {"foo": 1, "baz": [1,2,3,4]},
|
||||
"patch": [{"op": "move", "from": "/baz/1e0", "path": "/foo"}],
|
||||
"error": "move op shouldn't work with bad number" },
|
||||
|
||||
{ "comment": "test add with bad number should fail",
|
||||
"doc": ["foo", "sil"],
|
||||
"patch": [{"op": "add", "path": "/1e0", "value": "bar"}],
|
||||
"error": "add op shouldn't add to array with bad number" },
|
||||
|
||||
{ "comment": "missing 'value' parameter to add",
|
||||
"doc": [ 1 ],
|
||||
"patch": [ { "op": "add", "path": "/-" } ],
|
||||
"error": "missing 'value' parameter" },
|
||||
|
||||
{ "comment": "missing 'value' parameter to replace",
|
||||
"doc": [ 1 ],
|
||||
"patch": [ { "op": "replace", "path": "/0" } ],
|
||||
"error": "missing 'value' parameter" },
|
||||
|
||||
{ "comment": "missing 'value' parameter to test",
|
||||
"doc": [ null ],
|
||||
"patch": [ { "op": "test", "path": "/0" } ],
|
||||
"error": "missing 'value' parameter" },
|
||||
|
||||
{ "comment": "missing value parameter to test - where undef is falsy",
|
||||
"doc": [ false ],
|
||||
"patch": [ { "op": "test", "path": "/0" } ],
|
||||
"error": "missing 'value' parameter" },
|
||||
|
||||
{ "comment": "missing from parameter to copy",
|
||||
"doc": [ 1 ],
|
||||
"patch": [ { "op": "copy", "path": "/-" } ],
|
||||
"error": "missing 'from' parameter" },
|
||||
|
||||
{ "comment": "missing from parameter to move",
|
||||
"doc": { "foo": 1 },
|
||||
"patch": [ { "op": "move", "path": "" } ],
|
||||
"error": "missing 'from' parameter" },
|
||||
|
||||
{ "comment": "duplicate ops",
|
||||
"doc": { "foo": "bar" },
|
||||
"patch": [ { "op": "add", "path": "/baz", "value": "qux",
|
||||
"op": "move", "from":"/foo" } ],
|
||||
"error": "patch has two 'op' members",
|
||||
"disabled": true },
|
||||
|
||||
{ "comment": "unrecognized op should fail",
|
||||
"doc": {"foo": 1},
|
||||
"patch": [{"op": "spam", "path": "/foo", "value": 1}],
|
||||
"error": "Unrecognized op 'spam'" },
|
||||
|
||||
{ "comment": "test with bad array number that has leading zeros",
|
||||
"doc": ["foo", "bar"],
|
||||
"patch": [{"op": "test", "path": "/00", "value": "foo"}],
|
||||
"error": "test op should reject the array value, it has leading zeros" },
|
||||
|
||||
{ "comment": "test with bad array number that has leading zeros",
|
||||
"doc": ["foo", "bar"],
|
||||
"patch": [{"op": "test", "path": "/01", "value": "bar"}],
|
||||
"error": "test op should reject the array value, it has leading zeros" },
|
||||
|
||||
{ "comment": "Removing nonexistent field",
|
||||
"doc": {"foo" : "bar"},
|
||||
"patch": [{"op": "remove", "path": "/baz"}],
|
||||
"error": "removing a nonexistent field should fail" },
|
||||
|
||||
{ "comment": "Removing nonexistent index",
|
||||
"doc": ["foo", "bar"],
|
||||
"patch": [{"op": "remove", "path": "/2"}],
|
||||
"error": "removing a nonexistent index should fail" },
|
||||
|
||||
{ "comment": "Patch with different capitalisation than doc",
|
||||
"doc": {"foo":"bar"},
|
||||
"patch": [{"op": "add", "path": "/FOO", "value": "BAR"}],
|
||||
"expected": {"foo": "bar", "FOO": "BAR"}
|
||||
}
|
||||
|
||||
]
|
||||
243
tests/json_patch_tests.c
Normal file
243
tests/json_patch_tests.c
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
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"
|
||||
#include "../cJSON_Utils.h"
|
||||
|
||||
static cJSON *parse_test_file(const char * const filename)
|
||||
{
|
||||
char *file = NULL;
|
||||
cJSON *json = NULL;
|
||||
|
||||
file = read_file(filename);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(file, "Failed to read file.");
|
||||
|
||||
json = cJSON_Parse(file);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(json, "Failed to parse test json.");
|
||||
TEST_ASSERT_TRUE_MESSAGE(cJSON_IsArray(json), "Json is not an array.");
|
||||
|
||||
free(file);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
static cJSON_bool test_apply_patch(const cJSON * const test)
|
||||
{
|
||||
cJSON *doc = NULL;
|
||||
cJSON *patch = NULL;
|
||||
cJSON *expected = NULL;
|
||||
cJSON *error_element = NULL;
|
||||
cJSON *comment = NULL;
|
||||
cJSON *disabled = NULL;
|
||||
|
||||
cJSON *object = NULL;
|
||||
cJSON_bool successful = false;
|
||||
|
||||
/* extract all the data out of the test */
|
||||
comment = cJSON_GetObjectItemCaseSensitive(test, "comment");
|
||||
if (cJSON_IsString(comment))
|
||||
{
|
||||
printf("Testing \"%s\"\n", comment->valuestring);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Testing unkown\n");
|
||||
}
|
||||
|
||||
disabled = cJSON_GetObjectItemCaseSensitive(test, "disabled");
|
||||
if (cJSON_IsTrue(disabled))
|
||||
{
|
||||
printf("SKIPPED\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
doc = cJSON_GetObjectItemCaseSensitive(test, "doc");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(doc, "No \"doc\" in the test.");
|
||||
patch = cJSON_GetObjectItemCaseSensitive(test, "patch");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(patch, "No \"patch\"in the test.");
|
||||
/* Make a working copy of 'doc' */
|
||||
object = cJSON_Duplicate(doc, true);
|
||||
TEST_ASSERT_NOT_NULL(object);
|
||||
|
||||
expected = cJSON_GetObjectItemCaseSensitive(test, "expected");
|
||||
error_element = cJSON_GetObjectItemCaseSensitive(test, "error");
|
||||
if (error_element != NULL)
|
||||
{
|
||||
/* excepting an error */
|
||||
TEST_ASSERT_TRUE_MESSAGE(0 != cJSONUtils_ApplyPatchesCaseSensitive(object, patch), "Test didn't fail as it's supposed to.");
|
||||
|
||||
successful = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* apply the patch */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, cJSONUtils_ApplyPatchesCaseSensitive(object, patch), "Failed to apply patches.");
|
||||
successful = true;
|
||||
|
||||
if (expected != NULL)
|
||||
{
|
||||
successful = cJSON_Compare(object, expected, true);
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(object);
|
||||
|
||||
if (successful)
|
||||
{
|
||||
printf("OK\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("FAILED\n");
|
||||
}
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
static cJSON_bool test_generate_test(cJSON *test __attribute__((unused)))
|
||||
{
|
||||
cJSON *doc = NULL;
|
||||
cJSON *patch = NULL;
|
||||
cJSON *expected = NULL;
|
||||
cJSON *disabled = NULL;
|
||||
|
||||
cJSON *object = NULL;
|
||||
cJSON_bool successful = false;
|
||||
|
||||
char *printed_patch = NULL;
|
||||
|
||||
disabled = cJSON_GetObjectItemCaseSensitive(test, "disabled");
|
||||
if (cJSON_IsTrue(disabled))
|
||||
{
|
||||
printf("SKIPPED\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
doc = cJSON_GetObjectItemCaseSensitive(test, "doc");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(doc, "No \"doc\" in the test.");
|
||||
|
||||
/* Make a working copy of 'doc' */
|
||||
object = cJSON_Duplicate(doc, true);
|
||||
TEST_ASSERT_NOT_NULL(object);
|
||||
|
||||
expected = cJSON_GetObjectItemCaseSensitive(test, "expected");
|
||||
if (expected == NULL)
|
||||
{
|
||||
cJSON_Delete(object);
|
||||
/* if there is no expected output, this test doesn't make sense */
|
||||
return true;
|
||||
}
|
||||
|
||||
patch = cJSONUtils_GeneratePatchesCaseSensitive(doc, expected);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(patch, "Failed to generate patches.");
|
||||
|
||||
printed_patch = cJSON_Print(patch);
|
||||
printf("%s\n", printed_patch);
|
||||
free(printed_patch);
|
||||
|
||||
/* apply the generated patch */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, cJSONUtils_ApplyPatchesCaseSensitive(object, patch), "Failed to apply generated patch.");
|
||||
|
||||
successful = cJSON_Compare(object, expected, true);
|
||||
|
||||
cJSON_Delete(patch);
|
||||
cJSON_Delete(object);
|
||||
|
||||
if (successful)
|
||||
{
|
||||
printf("generated patch: OK\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("generated patch: FAILED\n");
|
||||
}
|
||||
|
||||
return successful;
|
||||
}
|
||||
|
||||
static void cjson_utils_should_pass_json_patch_test_tests(void)
|
||||
{
|
||||
cJSON *tests = parse_test_file("json-patch-tests/tests.json");
|
||||
cJSON *test = NULL;
|
||||
|
||||
cJSON_bool failed = false;
|
||||
cJSON_ArrayForEach(test, tests)
|
||||
{
|
||||
failed |= !test_apply_patch(test);
|
||||
failed |= !test_generate_test(test);
|
||||
}
|
||||
|
||||
cJSON_Delete(tests);
|
||||
|
||||
TEST_ASSERT_FALSE_MESSAGE(failed, "Some tests failed.");
|
||||
}
|
||||
|
||||
static void cjson_utils_should_pass_json_patch_test_spec_tests(void)
|
||||
{
|
||||
cJSON *tests = parse_test_file("json-patch-tests/spec_tests.json");
|
||||
cJSON *test = NULL;
|
||||
|
||||
cJSON_bool failed = false;
|
||||
cJSON_ArrayForEach(test, tests)
|
||||
{
|
||||
failed |= !test_apply_patch(test);
|
||||
failed |= !test_generate_test(test);
|
||||
}
|
||||
|
||||
cJSON_Delete(tests);
|
||||
|
||||
TEST_ASSERT_FALSE_MESSAGE(failed, "Some tests failed.");
|
||||
}
|
||||
|
||||
static void cjson_utils_should_pass_json_patch_test_cjson_utils_tests(void)
|
||||
{
|
||||
cJSON *tests = parse_test_file("json-patch-tests/cjson-utils-tests.json");
|
||||
cJSON *test = NULL;
|
||||
|
||||
cJSON_bool failed = false;
|
||||
cJSON_ArrayForEach(test, tests)
|
||||
{
|
||||
failed |= !test_apply_patch(test);
|
||||
failed |= !test_generate_test(test);
|
||||
}
|
||||
|
||||
cJSON_Delete(tests);
|
||||
|
||||
TEST_ASSERT_FALSE_MESSAGE(failed, "Some tests failed.");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(cjson_utils_should_pass_json_patch_test_tests);
|
||||
RUN_TEST(cjson_utils_should_pass_json_patch_test_spec_tests);
|
||||
RUN_TEST(cjson_utils_should_pass_json_patch_test_cjson_utils_tests);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
322
tests/misc_tests.c
Normal file
322
tests/misc_tests.c
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
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));
|
||||
}
|
||||
|
||||
static void cjson_should_not_parse_to_deeply_nested_jsons(void)
|
||||
{
|
||||
char deep_json[CJSON_NESTING_LIMIT + 1];
|
||||
size_t position = 0;
|
||||
|
||||
for (position = 0; position < sizeof(deep_json); position++)
|
||||
{
|
||||
deep_json[position] = '[';
|
||||
}
|
||||
deep_json[sizeof(deep_json) - 1] = '\0';
|
||||
|
||||
TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(deep_json), "To deep JSONs should not be parsed.");
|
||||
}
|
||||
|
||||
static void cjson_set_number_value_should_set_numbers(void)
|
||||
{
|
||||
cJSON number[1] = {{NULL, NULL, NULL, cJSON_Number, NULL, 0, 0, NULL}};
|
||||
|
||||
cJSON_SetNumberValue(number, 1.5);
|
||||
TEST_ASSERT_EQUAL(1, number->valueint);
|
||||
TEST_ASSERT_EQUAL_DOUBLE(1.5, number->valuedouble);
|
||||
|
||||
cJSON_SetNumberValue(number, -1.5);
|
||||
TEST_ASSERT_EQUAL(-1, number->valueint);
|
||||
TEST_ASSERT_EQUAL_DOUBLE(-1.5, number->valuedouble);
|
||||
|
||||
cJSON_SetNumberValue(number, 1 + (double)INT_MAX);
|
||||
TEST_ASSERT_EQUAL(INT_MAX, number->valueint);
|
||||
TEST_ASSERT_EQUAL_DOUBLE(1 + (double)INT_MAX, number->valuedouble);
|
||||
|
||||
cJSON_SetNumberValue(number, -1 + (double)INT_MIN);
|
||||
TEST_ASSERT_EQUAL(INT_MIN, number->valueint);
|
||||
TEST_ASSERT_EQUAL_DOUBLE(-1 + (double)INT_MIN, number->valuedouble);
|
||||
}
|
||||
|
||||
static void cjson_detach_item_via_pointer_should_detach_items(void)
|
||||
{
|
||||
cJSON list[4];
|
||||
cJSON parent[1];
|
||||
|
||||
memset(list, '\0', sizeof(list));
|
||||
|
||||
/* link the list */
|
||||
list[0].next = &(list[1]);
|
||||
list[1].next = &(list[2]);
|
||||
list[2].next = &(list[3]);
|
||||
|
||||
list[3].prev = &(list[2]);
|
||||
list[2].prev = &(list[1]);
|
||||
list[1].prev = &(list[0]);
|
||||
|
||||
parent->child = &list[0];
|
||||
|
||||
/* detach in the middle (list[1]) */
|
||||
TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[1])) == &(list[1]), "Failed to detach in the middle.");
|
||||
TEST_ASSERT_TRUE_MESSAGE((list[1].prev == NULL) && (list[1].next == NULL), "Didn't set pointers of detached item to NULL.");
|
||||
TEST_ASSERT_TRUE((list[0].next == &(list[2])) && (list[2].prev == &(list[0])));
|
||||
|
||||
/* detach beginning (list[0]) */
|
||||
TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[0])) == &(list[0]), "Failed to detach beginning.");
|
||||
TEST_ASSERT_TRUE_MESSAGE((list[0].prev == NULL) && (list[0].next == NULL), "Didn't set pointers of detached item to NULL.");
|
||||
TEST_ASSERT_TRUE_MESSAGE((list[2].prev == NULL) && (parent->child == &(list[2])), "Didn't set the new beginning.");
|
||||
|
||||
/* detach end (list[3])*/
|
||||
TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[3])) == &(list[3]), "Failed to detach end.");
|
||||
TEST_ASSERT_TRUE_MESSAGE((list[3].prev == NULL) && (list[3].next == NULL), "Didn't set pointers of detached item to NULL.");
|
||||
TEST_ASSERT_TRUE_MESSAGE((list[2].next == NULL) && (parent->child == &(list[2])), "Didn't set the new end");
|
||||
|
||||
/* detach single item (list[2]) */
|
||||
TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &list[2]) == &list[2], "Failed to detach single item.");
|
||||
TEST_ASSERT_TRUE_MESSAGE((list[2].prev == NULL) && (list[2].next == NULL), "Didn't set pointers of detached item to NULL.");
|
||||
TEST_ASSERT_NULL_MESSAGE(parent->child, "Child of the parent wasn't set to NULL.");
|
||||
}
|
||||
|
||||
static void cjson_replace_item_via_pointer_should_replace_items(void)
|
||||
{
|
||||
cJSON replacements[3];
|
||||
cJSON *beginning = NULL;
|
||||
cJSON *middle = NULL;
|
||||
cJSON *end = NULL;
|
||||
cJSON *array = NULL;
|
||||
|
||||
beginning = cJSON_CreateNull();
|
||||
TEST_ASSERT_NOT_NULL(beginning);
|
||||
middle = cJSON_CreateNull();
|
||||
TEST_ASSERT_NOT_NULL(middle);
|
||||
end = cJSON_CreateNull();
|
||||
TEST_ASSERT_NOT_NULL(end);
|
||||
|
||||
array = cJSON_CreateArray();
|
||||
TEST_ASSERT_NOT_NULL(array);
|
||||
|
||||
cJSON_AddItemToArray(array, beginning);
|
||||
cJSON_AddItemToArray(array, middle);
|
||||
cJSON_AddItemToArray(array, end);
|
||||
|
||||
|
||||
memset(replacements, '\0', sizeof(replacements));
|
||||
|
||||
/* replace beginning */
|
||||
TEST_ASSERT_TRUE(cJSON_ReplaceItemViaPointer(array, beginning, &(replacements[0])));
|
||||
TEST_ASSERT_NULL(replacements[0].prev);
|
||||
TEST_ASSERT_TRUE(replacements[0].next == middle);
|
||||
TEST_ASSERT_TRUE(middle->prev == &(replacements[0]));
|
||||
TEST_ASSERT_TRUE(array->child == &(replacements[0]));
|
||||
|
||||
/* replace middle */
|
||||
TEST_ASSERT_TRUE(cJSON_ReplaceItemViaPointer(array, middle, &(replacements[1])));
|
||||
TEST_ASSERT_TRUE(replacements[1].prev == &(replacements[0]));
|
||||
TEST_ASSERT_TRUE(replacements[1].next == end);
|
||||
TEST_ASSERT_TRUE(end->prev == &(replacements[1]));
|
||||
|
||||
/* replace end */
|
||||
TEST_ASSERT_TRUE(cJSON_ReplaceItemViaPointer(array, end, &(replacements[2])));
|
||||
TEST_ASSERT_TRUE(replacements[2].prev == &(replacements[1]));
|
||||
TEST_ASSERT_NULL(replacements[2].next);
|
||||
TEST_ASSERT_TRUE(replacements[1].next == &(replacements[2]));
|
||||
|
||||
cJSON_free(array);
|
||||
}
|
||||
|
||||
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);
|
||||
RUN_TEST(cjson_should_not_parse_to_deeply_nested_jsons);
|
||||
RUN_TEST(cjson_set_number_value_should_set_numbers);
|
||||
RUN_TEST(cjson_detach_item_via_pointer_should_detach_items);
|
||||
RUN_TEST(cjson_replace_item_via_pointer_should_replace_items);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
205
tests/old_utils_tests.c
Normal file
205
tests/old_utils_tests.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
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"
|
||||
#include "../cJSON_Utils.h"
|
||||
|
||||
/* JSON Apply Merge tests: */
|
||||
const char *merges[15][3] =
|
||||
{
|
||||
{"{\"a\":\"b\"}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"},
|
||||
{"{\"a\":\"b\"}", "{\"b\":\"c\"}", "{\"a\":\"b\",\"b\":\"c\"}"},
|
||||
{"{\"a\":\"b\"}", "{\"a\":null}", "{}"},
|
||||
{"{\"a\":\"b\",\"b\":\"c\"}", "{\"a\":null}", "{\"b\":\"c\"}"},
|
||||
{"{\"a\":[\"b\"]}", "{\"a\":\"c\"}", "{\"a\":\"c\"}"},
|
||||
{"{\"a\":\"c\"}", "{\"a\":[\"b\"]}", "{\"a\":[\"b\"]}"},
|
||||
{"{\"a\":{\"b\":\"c\"}}", "{\"a\":{\"b\":\"d\",\"c\":null}}", "{\"a\":{\"b\":\"d\"}}"},
|
||||
{"{\"a\":[{\"b\":\"c\"}]}", "{\"a\":[1]}", "{\"a\":[1]}"},
|
||||
{"[\"a\",\"b\"]", "[\"c\",\"d\"]", "[\"c\",\"d\"]"},
|
||||
{"{\"a\":\"b\"}", "[\"c\"]", "[\"c\"]"},
|
||||
{"{\"a\":\"foo\"}", "null", "null"},
|
||||
{"{\"a\":\"foo\"}", "\"bar\"", "\"bar\""},
|
||||
{"{\"e\":null}", "{\"a\":1}", "{\"e\":null,\"a\":1}"},
|
||||
{"[1,2]", "{\"a\":\"b\",\"c\":null}", "{\"a\":\"b\"}"},
|
||||
{"{}","{\"a\":{\"bb\":{\"ccc\":null}}}", "{\"a\":{\"bb\":{}}}"}
|
||||
};
|
||||
|
||||
static void json_pointer_tests(void)
|
||||
{
|
||||
cJSON *root = NULL;
|
||||
const char *json=
|
||||
"{"
|
||||
"\"foo\": [\"bar\", \"baz\"],"
|
||||
"\"\": 0,"
|
||||
"\"a/b\": 1,"
|
||||
"\"c%d\": 2,"
|
||||
"\"e^f\": 3,"
|
||||
"\"g|h\": 4,"
|
||||
"\"i\\\\j\": 5,"
|
||||
"\"k\\\"l\": 6,"
|
||||
"\" \": 7,"
|
||||
"\"m~n\": 8"
|
||||
"}";
|
||||
|
||||
root = cJSON_Parse(json);
|
||||
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, ""), root);
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/foo"), cJSON_GetObjectItem(root, "foo"));
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/foo/0"), cJSON_GetObjectItem(root, "foo")->child);
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/foo/0"), cJSON_GetObjectItem(root, "foo")->child);
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/"), cJSON_GetObjectItem(root, ""));
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/a~1b"), cJSON_GetObjectItem(root, "a/b"));
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/c%d"), cJSON_GetObjectItem(root, "c%d"));
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/c^f"), cJSON_GetObjectItem(root, "c^f"));
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/c|f"), cJSON_GetObjectItem(root, "c|f"));
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/i\\j"), cJSON_GetObjectItem(root, "i\\j"));
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/k\"l"), cJSON_GetObjectItem(root, "k\"l"));
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/ "), cJSON_GetObjectItem(root, " "));
|
||||
TEST_ASSERT_EQUAL_PTR(cJSONUtils_GetPointer(root, "/m~0n"), cJSON_GetObjectItem(root, "m~n"));
|
||||
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
|
||||
static void misc_tests(void)
|
||||
{
|
||||
/* Misc tests */
|
||||
int numbers[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
cJSON *object = NULL;
|
||||
cJSON *nums = NULL;
|
||||
cJSON *num6 = NULL;
|
||||
char *pointer = NULL;
|
||||
|
||||
printf("JSON Pointer construct\n");
|
||||
object = cJSON_CreateObject();
|
||||
nums = cJSON_CreateIntArray(numbers, 10);
|
||||
num6 = cJSON_GetArrayItem(nums, 6);
|
||||
cJSON_AddItemToObject(object, "numbers", nums);
|
||||
|
||||
pointer = cJSONUtils_FindPointerFromObjectTo(object, num6);
|
||||
TEST_ASSERT_EQUAL_STRING("/numbers/6", pointer);
|
||||
free(pointer);
|
||||
|
||||
pointer = cJSONUtils_FindPointerFromObjectTo(object, nums);
|
||||
TEST_ASSERT_EQUAL_STRING("/numbers", pointer);
|
||||
free(pointer);
|
||||
|
||||
pointer = cJSONUtils_FindPointerFromObjectTo(object, object);
|
||||
TEST_ASSERT_EQUAL_STRING("", pointer);
|
||||
free(pointer);
|
||||
|
||||
cJSON_Delete(object);
|
||||
}
|
||||
|
||||
static void sort_tests(void)
|
||||
{
|
||||
/* Misc tests */
|
||||
const char *random = "QWERTYUIOPASDFGHJKLZXCVBNM";
|
||||
char buf[2] = {'\0', '\0'};
|
||||
cJSON *sortme = NULL;
|
||||
size_t i = 0;
|
||||
cJSON *current_element = NULL;
|
||||
|
||||
/* JSON Sort test: */
|
||||
sortme = cJSON_CreateObject();
|
||||
for (i = 0; i < 26; i++)
|
||||
{
|
||||
buf[0] = random[i];
|
||||
cJSON_AddItemToObject(sortme, buf, cJSON_CreateNumber(1));
|
||||
}
|
||||
|
||||
cJSONUtils_SortObject(sortme);
|
||||
|
||||
/* check sorting */
|
||||
current_element = sortme->child->next;
|
||||
for (i = 1; (i < 26) && (current_element != NULL) && (current_element->prev != NULL); i++)
|
||||
{
|
||||
TEST_ASSERT_TRUE(current_element->string[0] >= current_element->prev->string[0]);
|
||||
current_element = current_element->next;
|
||||
}
|
||||
|
||||
cJSON_Delete(sortme);
|
||||
}
|
||||
|
||||
static void merge_tests(void)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *patchtext = NULL;
|
||||
char *after = NULL;
|
||||
|
||||
/* Merge tests: */
|
||||
printf("JSON Merge Patch tests\n");
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
cJSON *object_to_be_merged = cJSON_Parse(merges[i][0]);
|
||||
cJSON *patch = cJSON_Parse(merges[i][1]);
|
||||
patchtext = cJSON_PrintUnformatted(patch);
|
||||
object_to_be_merged = cJSONUtils_MergePatch(object_to_be_merged, patch);
|
||||
after = cJSON_PrintUnformatted(object_to_be_merged);
|
||||
TEST_ASSERT_EQUAL_STRING(merges[i][2], after);
|
||||
|
||||
free(patchtext);
|
||||
free(after);
|
||||
cJSON_Delete(object_to_be_merged);
|
||||
cJSON_Delete(patch);
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_merge_tests(void)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *patchedtext = NULL;
|
||||
|
||||
/* Generate Merge tests: */
|
||||
for (i = 0; i < 15; i++)
|
||||
{
|
||||
cJSON *from = cJSON_Parse(merges[i][0]);
|
||||
cJSON *to = cJSON_Parse(merges[i][2]);
|
||||
cJSON *patch = cJSONUtils_GenerateMergePatch(from,to);
|
||||
from = cJSONUtils_MergePatch(from,patch);
|
||||
patchedtext = cJSON_PrintUnformatted(from);
|
||||
TEST_ASSERT_EQUAL_STRING(merges[i][2], patchedtext);
|
||||
|
||||
cJSON_Delete(from);
|
||||
cJSON_Delete(to);
|
||||
cJSON_Delete(patch);
|
||||
free(patchedtext);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(json_pointer_tests);
|
||||
RUN_TEST(misc_tests);
|
||||
RUN_TEST(sort_tests);
|
||||
RUN_TEST(merge_tests);
|
||||
RUN_TEST(generate_merge_tests);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
166
tests/parse_array.c
Normal file
166
tests/parse_array.c
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
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_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)
|
||||
{
|
||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
buffer.content = (const unsigned char*)json;
|
||||
buffer.length = strlen(json) + sizeof("");
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
TEST_ASSERT_FALSE(parse_array(item, &buffer));
|
||||
assert_is_invalid(item);
|
||||
}
|
||||
|
||||
static void assert_parse_array(const char *json)
|
||||
{
|
||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
buffer.content = (const unsigned char*)json;
|
||||
buffer.length = strlen(json) + sizeof("");
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
TEST_ASSERT_TRUE(parse_array(item, &buffer));
|
||||
assert_is_array(item);
|
||||
}
|
||||
|
||||
static void parse_array_should_parse_empty_arrays(void)
|
||||
{
|
||||
assert_parse_array("[]");
|
||||
assert_has_no_child(item);
|
||||
|
||||
assert_parse_array("[\n\t]");
|
||||
assert_has_no_child(item);
|
||||
}
|
||||
|
||||
|
||||
static void parse_array_should_parse_arrays_with_one_element(void)
|
||||
{
|
||||
|
||||
assert_parse_array("[1]");
|
||||
assert_has_child(item);
|
||||
assert_has_type(item->child, cJSON_Number);
|
||||
reset(item);
|
||||
|
||||
assert_parse_array("[\"hello!\"]");
|
||||
assert_has_child(item);
|
||||
assert_has_type(item->child, cJSON_String);
|
||||
TEST_ASSERT_EQUAL_STRING("hello!", item->child->valuestring);
|
||||
reset(item);
|
||||
|
||||
assert_parse_array("[[]]");
|
||||
assert_has_child(item);
|
||||
assert_is_array(item->child);
|
||||
assert_has_no_child(item->child);
|
||||
reset(item);
|
||||
|
||||
assert_parse_array("[null]");
|
||||
assert_has_child(item);
|
||||
assert_has_type(item->child, cJSON_NULL);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void parse_array_should_parse_arrays_with_multiple_elements(void)
|
||||
{
|
||||
assert_parse_array("[1\t,\n2, 3]");
|
||||
assert_has_child(item);
|
||||
TEST_ASSERT_NOT_NULL(item->child->next);
|
||||
TEST_ASSERT_NOT_NULL(item->child->next->next);
|
||||
TEST_ASSERT_NULL(item->child->next->next->next);
|
||||
assert_has_type(item->child, cJSON_Number);
|
||||
assert_has_type(item->child->next, cJSON_Number);
|
||||
assert_has_type(item->child->next->next, cJSON_Number);
|
||||
reset(item);
|
||||
|
||||
{
|
||||
size_t i = 0;
|
||||
cJSON *node = NULL;
|
||||
int expected_types[7] =
|
||||
{
|
||||
cJSON_Number,
|
||||
cJSON_NULL,
|
||||
cJSON_True,
|
||||
cJSON_False,
|
||||
cJSON_Array,
|
||||
cJSON_String,
|
||||
cJSON_Object
|
||||
};
|
||||
assert_parse_array("[1, null, true, false, [], \"hello\", {}]");
|
||||
|
||||
node = item->child;
|
||||
for (
|
||||
i = 0;
|
||||
(i < (sizeof(expected_types)/sizeof(int)))
|
||||
&& (node != NULL);
|
||||
(void)i++, node = node->next)
|
||||
{
|
||||
TEST_ASSERT_BITS(0xFF, expected_types[i], node->type);
|
||||
}
|
||||
TEST_ASSERT_EQUAL_INT(i, 7);
|
||||
reset(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_array_should_not_parse_non_arrays(void)
|
||||
{
|
||||
assert_not_array("");
|
||||
assert_not_array("[");
|
||||
assert_not_array("]");
|
||||
assert_not_array("{\"hello\":[]}");
|
||||
assert_not_array("42");
|
||||
assert_not_array("3.14");
|
||||
assert_not_array("\"[]hello world!\n\"");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
memset(item, 0, sizeof(cJSON));
|
||||
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(parse_array_should_parse_empty_arrays);
|
||||
RUN_TEST(parse_array_should_parse_arrays_with_one_element);
|
||||
RUN_TEST(parse_array_should_parse_arrays_with_multiple_elements);
|
||||
RUN_TEST(parse_array_should_not_parse_non_arrays);
|
||||
return UNITY_END();
|
||||
}
|
||||
197
tests/parse_examples.c
Normal file
197
tests/parse_examples.c
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static cJSON *parse_file(const char *filename)
|
||||
{
|
||||
cJSON *parsed = NULL;
|
||||
char *content = read_file(filename);
|
||||
|
||||
parsed = cJSON_Parse(content);
|
||||
|
||||
if (content != NULL)
|
||||
{
|
||||
free(content);
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static void do_test(const char *test_name)
|
||||
{
|
||||
char *expected = NULL;
|
||||
char *actual = NULL;
|
||||
cJSON *tree = NULL;
|
||||
|
||||
size_t test_name_length = 0;
|
||||
/* path of the test input */
|
||||
char *test_path = NULL;
|
||||
/* path of the expected output */
|
||||
char *expected_path = NULL;
|
||||
|
||||
test_name_length = strlen(test_name);
|
||||
|
||||
/* allocate file paths */
|
||||
#define TEST_DIR_PATH "inputs/"
|
||||
test_path = (char*)malloc(sizeof(TEST_DIR_PATH) + test_name_length);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(test_path, "Failed to allocate test_path buffer.");
|
||||
expected_path = (char*)malloc(sizeof(TEST_DIR_PATH) + test_name_length + sizeof(".expected"));
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(expected_path, "Failed to allocate expected_path buffer.");
|
||||
|
||||
/* create file paths */
|
||||
sprintf(test_path, TEST_DIR_PATH"%s", test_name);
|
||||
sprintf(expected_path, TEST_DIR_PATH"%s.expected", test_name);
|
||||
|
||||
/* read expected output */
|
||||
expected = read_file(expected_path);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(expected, "Failed to read expected output.");
|
||||
|
||||
/* read and parse test */
|
||||
tree = parse_file(test_path);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to read of parse test.");
|
||||
|
||||
/* print the parsed tree */
|
||||
actual = cJSON_Print(tree);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(actual, "Failed to print tree back to JSON.");
|
||||
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING(expected, actual);
|
||||
|
||||
/* cleanup resources */
|
||||
if (expected != NULL)
|
||||
{
|
||||
free(expected);
|
||||
}
|
||||
if (tree != NULL)
|
||||
{
|
||||
cJSON_Delete(tree);
|
||||
}
|
||||
if (actual != NULL)
|
||||
{
|
||||
free(actual);
|
||||
}
|
||||
if (test_path != NULL)
|
||||
{
|
||||
free(test_path);
|
||||
}
|
||||
if (expected_path != NULL)
|
||||
{
|
||||
free(expected_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void file_test1_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test1");
|
||||
}
|
||||
|
||||
static void file_test2_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test2");
|
||||
}
|
||||
|
||||
static void file_test3_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test3");
|
||||
}
|
||||
|
||||
static void file_test4_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test4");
|
||||
}
|
||||
|
||||
static void file_test5_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test5");
|
||||
}
|
||||
|
||||
static void file_test6_should_not_be_parsed(void)
|
||||
{
|
||||
char *test6 = NULL;
|
||||
cJSON *tree = NULL;
|
||||
|
||||
test6 = read_file("inputs/test6");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(test6, "Failed to read test6 data.");
|
||||
|
||||
tree = cJSON_Parse(test6);
|
||||
TEST_ASSERT_NULL_MESSAGE(tree, "Should fail to parse what is not JSON.");
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(test6, cJSON_GetErrorPtr(), "Error pointer is incorrect.");
|
||||
|
||||
if (test6 != NULL)
|
||||
{
|
||||
free(test6);
|
||||
}
|
||||
if (tree != NULL)
|
||||
{
|
||||
cJSON_Delete(tree);
|
||||
}
|
||||
}
|
||||
|
||||
static void file_test7_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test7");
|
||||
}
|
||||
|
||||
static void file_test8_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test8");
|
||||
}
|
||||
|
||||
static void file_test9_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test9");
|
||||
}
|
||||
|
||||
static void file_test10_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test10");
|
||||
}
|
||||
|
||||
static void file_test11_should_be_parsed_and_printed(void)
|
||||
{
|
||||
do_test("test11");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(file_test1_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test2_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test3_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test4_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test5_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test6_should_not_be_parsed);
|
||||
RUN_TEST(file_test7_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test8_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test9_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test10_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test11_should_be_parsed_and_printed);
|
||||
return UNITY_END();
|
||||
}
|
||||
73
tests/parse_hex4.c
Normal file
73
tests/parse_hex4.c
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static void parse_hex4_should_parse_all_combinations(void)
|
||||
{
|
||||
unsigned int number = 0;
|
||||
unsigned char digits_lower[5];
|
||||
unsigned char digits_upper[5];
|
||||
/* test all combinations */
|
||||
for (number = 0; number <= 0xFFFF; number++)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(4, sprintf((char*)digits_lower, "%.4x", number), "sprintf failed.");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(4, sprintf((char*)digits_upper, "%.4X", number), "sprintf failed.");
|
||||
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(number, parse_hex4(digits_lower), "Failed to parse lowercase digits.");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(number, parse_hex4(digits_upper), "Failed to parse uppercase digits.");
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_hex4_should_parse_mixed_case(void)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beef"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beeF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beEf"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"beEF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEef"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEeF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEEf"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"bEEF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"Beef"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BeeF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BeEf"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BeEF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEef"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEeF"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEEf"));
|
||||
TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEEF"));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(parse_hex4_should_parse_all_combinations);
|
||||
RUN_TEST(parse_hex4_should_parse_mixed_case);
|
||||
return UNITY_END();
|
||||
}
|
||||
110
tests/parse_number.c
Normal file
110
tests/parse_number.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
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)
|
||||
{
|
||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
buffer.content = (const unsigned char*)string;
|
||||
buffer.length = strlen(string) + sizeof("");
|
||||
|
||||
TEST_ASSERT_TRUE(parse_number(item, &buffer));
|
||||
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();
|
||||
}
|
||||
176
tests/parse_object.c
Normal file
176
tests/parse_object.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static cJSON item[1];
|
||||
|
||||
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)
|
||||
{
|
||||
parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
parsebuffer.content = (const unsigned char*)json;
|
||||
parsebuffer.length = strlen(json) + sizeof("");
|
||||
parsebuffer.hooks = global_hooks;
|
||||
|
||||
TEST_ASSERT_FALSE(parse_object(item, &parsebuffer));
|
||||
assert_is_invalid(item);
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void assert_parse_object(const char *json)
|
||||
{
|
||||
parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
parsebuffer.content = (const unsigned char*)json;
|
||||
parsebuffer.length = strlen(json) + sizeof("");
|
||||
parsebuffer.hooks = global_hooks;
|
||||
|
||||
TEST_ASSERT_TRUE(parse_object(item, &parsebuffer));
|
||||
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();
|
||||
}
|
||||
135
tests/parse_string.c
Normal file
135
tests/parse_string.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
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_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)
|
||||
{
|
||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
buffer.content = (const unsigned char*)string;
|
||||
buffer.length = strlen(string) + sizeof("");
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(parse_string(item, &buffer), "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;
|
||||
}
|
||||
|
||||
static void assert_not_parse_string(const char * const string)
|
||||
{
|
||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
buffer.content = (const unsigned char*)string;
|
||||
buffer.length = strlen(string) + sizeof("");
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
TEST_ASSERT_FALSE_MESSAGE(parse_string(item, &buffer), "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();
|
||||
}
|
||||
112
tests/parse_value.c
Normal file
112
tests/parse_value.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
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_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)
|
||||
{
|
||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
buffer.content = (const unsigned char*) string;
|
||||
buffer.length = strlen(string) + sizeof("");
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
TEST_ASSERT_TRUE(parse_value(item, &buffer));
|
||||
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();
|
||||
}
|
||||
82
tests/parse_with_opts.c
Normal file
82
tests/parse_with_opts.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
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 parse_with_opts_should_handle_null(void)
|
||||
{
|
||||
const char *error_pointer = NULL;
|
||||
cJSON *item = NULL;
|
||||
TEST_ASSERT_NULL_MESSAGE(cJSON_ParseWithOpts(NULL, &error_pointer, false), "Failed to handle NULL input.");
|
||||
item = cJSON_ParseWithOpts("{}", NULL, false);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(item, "Failed to handle NULL error pointer.");
|
||||
cJSON_Delete(item);
|
||||
TEST_ASSERT_NULL_MESSAGE(cJSON_ParseWithOpts(NULL, NULL, false), "Failed to handle both NULL.");
|
||||
TEST_ASSERT_NULL_MESSAGE(cJSON_ParseWithOpts("{", NULL, false), "Failed to handle NULL error pointer with parse error.");
|
||||
}
|
||||
|
||||
static void parse_with_opts_should_handle_empty_strings(void)
|
||||
{
|
||||
const char empty_string[] = "";
|
||||
const char *error_pointer = NULL;
|
||||
TEST_ASSERT_NULL(cJSON_ParseWithOpts(empty_string, NULL, false));
|
||||
error_pointer = cJSON_GetErrorPtr();
|
||||
TEST_ASSERT_EQUAL_INT(0, error_pointer - empty_string);
|
||||
TEST_ASSERT_NULL(cJSON_ParseWithOpts(empty_string, &error_pointer, false));
|
||||
TEST_ASSERT_EQUAL_INT(0, error_pointer - empty_string);
|
||||
}
|
||||
|
||||
static void parse_with_opts_should_require_null_if_requested(void)
|
||||
{
|
||||
cJSON *item = cJSON_ParseWithOpts("{}", NULL, true);
|
||||
TEST_ASSERT_NOT_NULL(item);
|
||||
cJSON_Delete(item);
|
||||
item = cJSON_ParseWithOpts("{} \n", NULL, true);
|
||||
TEST_ASSERT_NOT_NULL(item);
|
||||
cJSON_Delete(item);
|
||||
TEST_ASSERT_NULL(cJSON_ParseWithOpts("{}x", NULL, true));
|
||||
}
|
||||
|
||||
static void parse_with_opts_should_return_parse_end(void)
|
||||
{
|
||||
const char json[] = "[] empty array XD";
|
||||
const char *parse_end = NULL;
|
||||
|
||||
cJSON *item = cJSON_ParseWithOpts(json, &parse_end, false);
|
||||
TEST_ASSERT_NOT_NULL(item);
|
||||
TEST_ASSERT_EQUAL_INT(2, parse_end - json);
|
||||
cJSON_Delete(item);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(parse_with_opts_should_handle_null);
|
||||
RUN_TEST(parse_with_opts_should_handle_empty_strings);
|
||||
RUN_TEST(parse_with_opts_should_require_null_if_requested);
|
||||
RUN_TEST(parse_with_opts_should_return_parse_end);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
100
tests/print_array.c
Normal file
100
tests/print_array.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
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];
|
||||
|
||||
cJSON item[1];
|
||||
|
||||
printbuffer formatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
printbuffer unformatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
|
||||
parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
parsebuffer.content = (const unsigned char*)input;
|
||||
parsebuffer.length = strlen(input) + sizeof("");
|
||||
parsebuffer.hooks = global_hooks;
|
||||
|
||||
/* buffer for formatted printing */
|
||||
formatted_buffer.buffer = printed_formatted;
|
||||
formatted_buffer.length = sizeof(printed_formatted);
|
||||
formatted_buffer.offset = 0;
|
||||
formatted_buffer.noalloc = true;
|
||||
formatted_buffer.hooks = global_hooks;
|
||||
|
||||
/* buffer for unformatted printing */
|
||||
unformatted_buffer.buffer = printed_unformatted;
|
||||
unformatted_buffer.length = sizeof(printed_unformatted);
|
||||
unformatted_buffer.offset = 0;
|
||||
unformatted_buffer.noalloc = true;
|
||||
unformatted_buffer.hooks = global_hooks;
|
||||
|
||||
memset(item, 0, sizeof(item));
|
||||
TEST_ASSERT_TRUE_MESSAGE(parse_array(item, &parsebuffer), "Failed to parse array.");
|
||||
|
||||
unformatted_buffer.format = false;
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_array(item, &unformatted_buffer), "Failed to print unformatted string.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted array is not correct.");
|
||||
|
||||
formatted_buffer.format = true;
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_array(item, &formatted_buffer), "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();
|
||||
}
|
||||
104
tests/print_number.c
Normal file
104
tests/print_number.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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 = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
buffer.buffer = printed;
|
||||
buffer.length = sizeof(printed);
|
||||
buffer.offset = 0;
|
||||
buffer.noalloc = true;
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
memset(item, 0, sizeof(item));
|
||||
cJSON_SetNumberValue(item, input);
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_number(item, &buffer), "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("1e-09", 10e-10);
|
||||
assert_print_number("1000000000000", 10e11);
|
||||
assert_print_number("1.23e+129", 123e+127);
|
||||
assert_print_number("1.23e-126", 123e-128);
|
||||
}
|
||||
|
||||
static void print_number_should_print_negative_reals(void)
|
||||
{
|
||||
assert_print_number("-0.0123", -0.0123);
|
||||
assert_print_number("-1e-09", -10e-10);
|
||||
assert_print_number("-1e+21", -10e20);
|
||||
assert_print_number("-1.23e+129", -123e+127);
|
||||
assert_print_number("-1.23e-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); */
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
101
tests/print_object.c
Normal file
101
tests/print_object.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
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];
|
||||
|
||||
cJSON item[1];
|
||||
|
||||
printbuffer formatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
printbuffer unformatted_buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
|
||||
/* buffer for parsing */
|
||||
parsebuffer.content = (const unsigned char*)input;
|
||||
parsebuffer.length = strlen(input) + sizeof("");
|
||||
parsebuffer.hooks = global_hooks;
|
||||
|
||||
/* buffer for formatted printing */
|
||||
formatted_buffer.buffer = printed_formatted;
|
||||
formatted_buffer.length = sizeof(printed_formatted);
|
||||
formatted_buffer.offset = 0;
|
||||
formatted_buffer.noalloc = true;
|
||||
formatted_buffer.hooks = global_hooks;
|
||||
|
||||
/* buffer for unformatted printing */
|
||||
unformatted_buffer.buffer = printed_unformatted;
|
||||
unformatted_buffer.length = sizeof(printed_unformatted);
|
||||
unformatted_buffer.offset = 0;
|
||||
unformatted_buffer.noalloc = true;
|
||||
unformatted_buffer.hooks = global_hooks;
|
||||
|
||||
memset(item, 0, sizeof(item));
|
||||
TEST_ASSERT_TRUE_MESSAGE(parse_object(item, &parsebuffer), "Failed to parse object.");
|
||||
|
||||
unformatted_buffer.format = false;
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_object(item, &unformatted_buffer), "Failed to print unformatted string.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted object is not correct.");
|
||||
|
||||
formatted_buffer.format = true;
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_object(item, &formatted_buffer), "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();
|
||||
}
|
||||
78
tests/print_string.c
Normal file
78
tests/print_string.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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 = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
buffer.buffer = printed;
|
||||
buffer.length = sizeof(printed);
|
||||
buffer.offset = 0;
|
||||
buffer.noalloc = true;
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_string_ptr((const unsigned char*)input, &buffer), "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();
|
||||
}
|
||||
107
tests/print_value.c
Normal file
107
tests/print_value.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
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];
|
||||
cJSON item[1];
|
||||
printbuffer buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
parse_buffer parsebuffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
buffer.buffer = printed;
|
||||
buffer.length = sizeof(printed);
|
||||
buffer.offset = 0;
|
||||
buffer.noalloc = true;
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
parsebuffer.content = (const unsigned char*)input;
|
||||
parsebuffer.length = strlen(input) + sizeof("");
|
||||
parsebuffer.hooks = global_hooks;
|
||||
|
||||
memset(item, 0, sizeof(item));
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(parse_value(item, &parsebuffer), "Failed to parse value.");
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(print_value(item, &buffer), "Failed to print value.");
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(input, buffer.buffer, "Printed value is not as expected.");
|
||||
|
||||
reset(item);
|
||||
}
|
||||
|
||||
static void print_value_should_print_null(void)
|
||||
{
|
||||
assert_print_value("null");
|
||||
}
|
||||
|
||||
static void print_value_should_print_true(void)
|
||||
{
|
||||
assert_print_value("true");
|
||||
}
|
||||
|
||||
static void print_value_should_print_false(void)
|
||||
{
|
||||
assert_print_value("false");
|
||||
}
|
||||
|
||||
static void print_value_should_print_number(void)
|
||||
{
|
||||
assert_print_value("1.5");
|
||||
}
|
||||
|
||||
static void print_value_should_print_string(void)
|
||||
{
|
||||
assert_print_value("\"\"");
|
||||
assert_print_value("\"hello\"");
|
||||
}
|
||||
|
||||
static void print_value_should_print_array(void)
|
||||
{
|
||||
assert_print_value("[]");
|
||||
}
|
||||
|
||||
static void print_value_should_print_object(void)
|
||||
{
|
||||
assert_print_value("{}");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* initialize cJSON item */
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(print_value_should_print_null);
|
||||
RUN_TEST(print_value_should_print_true);
|
||||
RUN_TEST(print_value_should_print_false);
|
||||
RUN_TEST(print_value_should_print_number);
|
||||
RUN_TEST(print_value_should_print_string);
|
||||
RUN_TEST(print_value_should_print_array);
|
||||
RUN_TEST(print_value_should_print_object);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
30
tests/unity/.gitattributes
vendored
Normal file
30
tests/unity/.gitattributes
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
* text=auto
|
||||
|
||||
# These files are text and should be normalized (convert crlf to lf)
|
||||
*.rb text
|
||||
*.test text
|
||||
*.c text
|
||||
*.cpp text
|
||||
*.h text
|
||||
*.txt text
|
||||
*.yml text
|
||||
*.s79 text
|
||||
*.bat text
|
||||
*.xcl text
|
||||
*.inc text
|
||||
*.info text
|
||||
*.md text
|
||||
makefile text
|
||||
rakefile text
|
||||
|
||||
|
||||
#These files are binary and should not be normalized
|
||||
*.doc binary
|
||||
*.odt binary
|
||||
*.pdf binary
|
||||
*.ewd binary
|
||||
*.eww binary
|
||||
*.dni binary
|
||||
*.wsdt binary
|
||||
*.dbgdt binary
|
||||
*.mac binary
|
||||
9
tests/unity/.gitignore
vendored
Normal file
9
tests/unity/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
build/
|
||||
test/sandbox
|
||||
.DS_Store
|
||||
examples/example_1/test1.exe
|
||||
examples/example_1/test2.exe
|
||||
examples/example_2/all_tests.exe
|
||||
examples/example_1/test1.out
|
||||
examples/example_1/test2.out
|
||||
examples/example_2/all_tests.out
|
||||
27
tests/unity/.travis.yml
Normal file
27
tests/unity/.travis.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
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
|
||||
- gem install rubocop
|
||||
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
|
||||
220
tests/unity/README.md
Normal file
220
tests/unity/README.md
Normal file
@@ -0,0 +1,220 @@
|
||||
Unity Test API
|
||||
==============
|
||||
|
||||
[](https://travis-ci.org/ThrowTheSwitch/Unity)
|
||||
__Copyright (c) 2007 - 2017 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).
|
||||
|
||||
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.
|
||||
|
||||
Arrays
|
||||
------
|
||||
|
||||
_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)
|
||||
|
||||
_EACH_EQUAL
|
||||
|
||||
Another array comparison option is to check that EVERY element of an array is equal to a single expected
|
||||
value. You do this by specifying the EACH_EQUAL macro. For example:
|
||||
|
||||
TEST_ASSERT_EACH_EQUAL_INT32(expected, actual, elements)
|
||||
|
||||
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.
|
||||
|
||||
118
tests/unity/auto/colour_prompt.rb
Normal file
118
tests/unity/auto/colour_prompt.rb
Normal file
@@ -0,0 +1,118 @@
|
||||
# ==========================================
|
||||
# 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
|
||||
return unless RUBY_PLATFORM =~ /(win|w)32$/
|
||||
get_std_handle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L')
|
||||
@set_console_txt_attrb =
|
||||
Win32API.new('kernel32', 'SetConsoleTextAttribute', %w(L N), 'I')
|
||||
@hout = get_std_handle.call(-11)
|
||||
end
|
||||
|
||||
def change_to(new_colour)
|
||||
if RUBY_PLATFORM =~ /(win|w)32$/
|
||||
@set_console_txt_attrb.call(@hout, win32_colour(new_colour))
|
||||
else
|
||||
"\033[30;#{posix_colour(new_colour)};22m"
|
||||
end
|
||||
end
|
||||
|
||||
def win32_colour(colour)
|
||||
case colour
|
||||
when :black then 0
|
||||
when :dark_blue then 1
|
||||
when :dark_green then 2
|
||||
when :dark_cyan then 3
|
||||
when :dark_red then 4
|
||||
when :dark_purple then 5
|
||||
when :dark_yellow, :narrative then 6
|
||||
when :default_white, :default, :dark_white then 7
|
||||
when :silver then 8
|
||||
when :blue then 9
|
||||
when :green, :success then 10
|
||||
when :cyan, :output then 11
|
||||
when :red, :failure then 12
|
||||
when :purple then 13
|
||||
when :yellow then 14
|
||||
when :white then 15
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
def posix_colour(colour)
|
||||
# ANSI Escape Codes - Foreground colors
|
||||
# | Code | Color |
|
||||
# | 39 | Default foreground color |
|
||||
# | 30 | Black |
|
||||
# | 31 | Red |
|
||||
# | 32 | Green |
|
||||
# | 33 | Yellow |
|
||||
# | 34 | Blue |
|
||||
# | 35 | Magenta |
|
||||
# | 36 | Cyan |
|
||||
# | 37 | Light gray |
|
||||
# | 90 | Dark gray |
|
||||
# | 91 | Light red |
|
||||
# | 92 | Light green |
|
||||
# | 93 | Light yellow |
|
||||
# | 94 | Light blue |
|
||||
# | 95 | Light magenta |
|
||||
# | 96 | Light cyan |
|
||||
# | 97 | White |
|
||||
|
||||
case colour
|
||||
when :black then 30
|
||||
when :red, :failure then 31
|
||||
when :green, :success then 32
|
||||
when :yellow then 33
|
||||
when :blue, :narrative then 34
|
||||
when :purple, :magenta then 35
|
||||
when :cyan, :output then 36
|
||||
when :white, :default_white then 37
|
||||
when :default then 39
|
||||
else
|
||||
39
|
||||
end
|
||||
end
|
||||
|
||||
def out_c(mode, colour, str)
|
||||
case RUBY_PLATFORM
|
||||
when /(win|w)32$/
|
||||
change_to(colour)
|
||||
$stdout.puts str if mode == :puts
|
||||
$stdout.print str if mode == :print
|
||||
change_to(:default_white)
|
||||
else
|
||||
$stdout.puts("#{change_to(colour)}#{str}\033[0m") if mode == :puts
|
||||
$stdout.print("#{change_to(colour)}#{str}\033[0m") if mode == :print
|
||||
end
|
||||
end
|
||||
end # ColourCommandLine
|
||||
|
||||
def colour_puts(role, str)
|
||||
ColourCommandLine.new.out_c(:puts, role, str)
|
||||
end
|
||||
|
||||
def colour_print(role, str)
|
||||
ColourCommandLine.new.out_c(:print, role, str)
|
||||
end
|
||||
39
tests/unity/auto/colour_reporter.rb
Normal file
39
tests/unity/auto/colour_reporter.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
# ==========================================
|
||||
# Unity Project - A Test Framework for C
|
||||
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
||||
# [Released under MIT License. Please refer to license.txt for details]
|
||||
# ==========================================
|
||||
|
||||
require "#{File.expand_path(File.dirname(__FILE__))}/colour_prompt"
|
||||
|
||||
$colour_output = true
|
||||
|
||||
def report(message)
|
||||
if !$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
|
||||
Regexp.last_match(1).to_i.zero? ? :green : :red
|
||||
when /PASS/
|
||||
:green
|
||||
when /^OK$/
|
||||
:green
|
||||
when /(?:FAIL|ERROR)/
|
||||
:red
|
||||
when /IGNORE/
|
||||
:yellow
|
||||
when /^(?:Creating|Compiling|Linking)/
|
||||
:white
|
||||
else
|
||||
:silver
|
||||
end
|
||||
colour_puts(colour, line)
|
||||
end
|
||||
end
|
||||
$stdout.flush
|
||||
$stderr.flush
|
||||
end
|
||||
36
tests/unity/auto/generate_config.yml
Normal file
36
tests/unity/auto/generate_config.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
#this is a sample configuration file for generate_module
|
||||
#you would use it by calling generate_module with the -ygenerate_config.yml option
|
||||
#files like this are useful for customizing generate_module to your environment
|
||||
:generate_module:
|
||||
:defaults:
|
||||
#these defaults are used in place of any missing options at the command line
|
||||
:path_src: ../src/
|
||||
:path_inc: ../src/
|
||||
:path_tst: ../test/
|
||||
:update_svn: true
|
||||
:includes:
|
||||
#use [] for no additional includes, otherwise list the includes on separate lines
|
||||
:src:
|
||||
- Defs.h
|
||||
- Board.h
|
||||
:inc: []
|
||||
:tst:
|
||||
- Defs.h
|
||||
- Board.h
|
||||
- Exception.h
|
||||
:boilerplates:
|
||||
#these are inserted at the top of generated files.
|
||||
#just comment out or remove if not desired.
|
||||
#use %1$s where you would like the file name to appear (path/extension not included)
|
||||
:src: |
|
||||
//-------------------------------------------
|
||||
// %1$s.c
|
||||
//-------------------------------------------
|
||||
:inc: |
|
||||
//-------------------------------------------
|
||||
// %1$s.h
|
||||
//-------------------------------------------
|
||||
:tst: |
|
||||
//-------------------------------------------
|
||||
// Test%1$s.c : Units tests for %1$s.c
|
||||
//-------------------------------------------
|
||||
308
tests/unity/auto/generate_module.rb
Normal file
308
tests/unity/auto/generate_module.rb
Normal file
@@ -0,0 +1,308 @@
|
||||
# ==========================================
|
||||
# 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 ||= '#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");
|
||||
}
|
||||
'.freeze
|
||||
|
||||
# TEMPLATE_SRC
|
||||
TEMPLATE_SRC ||= '%2$s#include "%1$s.h"
|
||||
'.freeze
|
||||
|
||||
# TEMPLATE_INC
|
||||
TEMPLATE_INC ||= '#ifndef _%3$s_H
|
||||
#define _%3$s_H
|
||||
%2$s
|
||||
|
||||
#endif // _%3$s_H
|
||||
'.freeze
|
||||
|
||||
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 = default_options
|
||||
unless config_file.nil? || 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
|
||||
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
|
||||
triad.select! { |v| v[:inc] == :tst } if pattern == 'test'
|
||||
|
||||
# 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| format(f, module_name) })
|
||||
when :inc then (@options[:includes][:inc] || [])
|
||||
when :tst then (@options[:includes][:tst] || []) | (pattern_traits[:inc].map { |f| format("#{@options[:mock_prefix]}#{f}", module_name) })
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
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|
|
||||
all_files_exist = false unless File.exist?(file[:path])
|
||||
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 { |ff| "#include \"#{ff}\"\n" }.join,
|
||||
file[:name].upcase])
|
||||
end
|
||||
if @options[:update_svn]
|
||||
`svn add \"#{file[:path]}\"`
|
||||
if $!.exitstatus.zero?
|
||||
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] = Regexp.last_match(1)
|
||||
when /^-s\"?(.+)\"?/ then options[:path_src] = Regexp.last_match(1)
|
||||
when /^-i\"?(.+)\"?/ then options[:path_inc] = Regexp.last_match(1)
|
||||
when /^-t\"?(.+)\"?/ then options[:path_tst] = Regexp.last_match(1)
|
||||
when /^-n\"?(.+)\"?/ then options[:naming] = Regexp.last_match(1)
|
||||
when /^-y\"?(.+)\"?/ then options = UnityModuleGenerator.grab_config(Regexp.last_match(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 = [].freeze
|
||||
else
|
||||
raise "ERROR: Unknown option specified '#{arg}'"
|
||||
end
|
||||
end
|
||||
|
||||
unless 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
|
||||
437
tests/unity/auto/generate_test_runner.rb
Normal file
437
tests/unity/auto/generate_test_runner.rb
Normal file
@@ -0,0 +1,437 @@
|
||||
# ==========================================
|
||||
# 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]
|
||||
# ==========================================
|
||||
|
||||
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',
|
||||
mock_prefix: 'Mock',
|
||||
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 = default_options
|
||||
unless config_file.nil? || 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
|
||||
options
|
||||
end
|
||||
|
||||
def run(input_file, output_file, options = nil)
|
||||
@options.merge!(options) unless options.nil?
|
||||
|
||||
# pull required data from source file
|
||||
source = File.read(input_file)
|
||||
source = source.force_encoding('ISO-8859-1').encode('utf-8', replace: nil)
|
||||
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?
|
||||
all_files_used += headers[:linkonly] unless headers[:linkonly].empty?
|
||||
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(output)
|
||||
create_suite_teardown(output)
|
||||
create_reset(output, used_mocks)
|
||||
create_main(output, input_file, tests, used_mocks)
|
||||
end
|
||||
|
||||
return unless @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
|
||||
|
||||
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
|
||||
next unless line =~ /^((?:\s*TEST_CASE\s*\(.*?\)\s*)*)\s*void\s+((?:#{@options[:test_prefix]}).*)\s*\(\s*(.*)\s*\)/
|
||||
arguments = Regexp.last_match(1)
|
||||
name = Regexp.last_match(2)
|
||||
call = Regexp.last_match(3)
|
||||
params = Regexp.last_match(4)
|
||||
args = nil
|
||||
if @options[:use_param_tests] && !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
|
||||
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|
|
||||
next unless line =~ /#{tests_and_line_numbers[i][:test]}/
|
||||
source_index += index
|
||||
tests_and_line_numbers[i][:line_number] = source_index + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
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}>" },
|
||||
linkonly: source.scan(/^TEST_FILE\(\s*\"\s*(.+)\.[cC]\w*\s*\"/).flatten
|
||||
}
|
||||
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 =~ /^#{@options[:mock_prefix]}/i
|
||||
end
|
||||
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]}.h\"")
|
||||
output.puts('#include "cmock.h"') unless mocks.empty?
|
||||
output.puts('#include <setjmp.h>')
|
||||
output.puts('#include <stdio.h>')
|
||||
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
|
||||
output.puts('#include "CException.h"') if @options[:plugins].include?(:cexception)
|
||||
|
||||
return unless @options[:enforce_strict_ordering]
|
||||
|
||||
output.puts('')
|
||||
output.puts('int GlobalExpectCount;')
|
||||
output.puts('int GlobalVerifyOrder;')
|
||||
output.puts('char* GlobalOrderError;')
|
||||
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)
|
||||
return if 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
|
||||
|
||||
def create_suite_setup(output)
|
||||
return if @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
|
||||
|
||||
def create_suite_teardown(output)
|
||||
return if @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
|
||||
|
||||
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]).to_s
|
||||
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? || 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? || 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]}.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? || 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: [] }
|
||||
|
||||
# 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[Regexp.last_match(1).to_sym] = Regexp.last_match(2)
|
||||
true
|
||||
when /\.*\.h/
|
||||
options[:includes] << arg
|
||||
true
|
||||
else false
|
||||
end
|
||||
end
|
||||
|
||||
# make sure there is at least one parameter left (the input file)
|
||||
unless 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') unless ARGV[1]
|
||||
|
||||
UnityTestRunnerGenerator.new(options).run(ARGV[0], ARGV[1])
|
||||
end
|
||||
220
tests/unity/auto/parse_output.rb
Normal file
220
tests/unity/auto/parse_output.rb
Normal file
@@ -0,0 +1,220 @@
|
||||
#============================================================
|
||||
# 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
|
||||
def initialize
|
||||
@test_flag = false
|
||||
@xml_out = false
|
||||
@array_list = false
|
||||
@total_tests = false
|
||||
@class_index = false
|
||||
end
|
||||
|
||||
# Set the flag to indicate if there will be an XML output file or not
|
||||
def set_xml_output
|
||||
@xml_out = true
|
||||
end
|
||||
|
||||
# if write our output to XML
|
||||
def write_xml_output
|
||||
output = File.open('report.xml', 'w')
|
||||
output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
@array_list.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 test_suite_verify(test_suite_name)
|
||||
return if @test_flag
|
||||
|
||||
@test_flag = true
|
||||
# Split the path name
|
||||
test_name = test_suite_name.split('/')
|
||||
# Remove the extension
|
||||
base_name = test_name[test_name.size - 1].split('.')
|
||||
@test_suite = 'test.' + base_name[0]
|
||||
printf "New Test: %s\n", @test_suite
|
||||
end
|
||||
|
||||
# Test was flagged as having passed so format the output
|
||||
def test_passed(array)
|
||||
last_item = array.length - 1
|
||||
test_name = array[last_item - 1]
|
||||
test_suite_verify(array[@class_name])
|
||||
printf "%-40s PASS\n", test_name
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
@array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '"/>'
|
||||
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 test_passed_unity_fixture(array)
|
||||
test_suite = array[0].sub('TEST(', '')
|
||||
test_suite = test_suite.sub(',', '')
|
||||
test_name = array[1].sub(')', '')
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
@array_list.push ' <testcase classname="' + test_suite + '" name="' + test_name + '"/>'
|
||||
end
|
||||
|
||||
# Test was flagged as being ingored so format the output
|
||||
def test_ignored(array)
|
||||
last_item = array.length - 1
|
||||
test_name = array[last_item - 2]
|
||||
reason = array[last_item].chomp
|
||||
test_suite_verify(array[@class_name])
|
||||
printf "%-40s IGNORED\n", test_name
|
||||
|
||||
if test_name.start_with? 'TEST('
|
||||
array2 = test_name.split(' ')
|
||||
@test_suite = array2[0].sub('TEST(', '')
|
||||
@test_suite = @test_suite.sub(',', '')
|
||||
test_name = array2[1].sub(')', '')
|
||||
end
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
@array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '">'
|
||||
@array_list.push ' <skipped type="TEST IGNORED"> ' + reason + ' </skipped>'
|
||||
@array_list.push ' </testcase>'
|
||||
end
|
||||
|
||||
# Test was flagged as having failed so format the line
|
||||
def test_failed(array)
|
||||
last_item = array.length - 1
|
||||
test_name = array[last_item - 2]
|
||||
reason = array[last_item].chomp + ' at line: ' + array[last_item - 3]
|
||||
test_suite_verify(array[@class_name])
|
||||
printf "%-40s FAILED\n", test_name
|
||||
|
||||
if test_name.start_with? 'TEST('
|
||||
array2 = test_name.split(' ')
|
||||
@test_suite = array2[0].sub('TEST(', '')
|
||||
@test_suite = @test_suite.sub(',', '')
|
||||
test_name = array2[1].sub(')', '')
|
||||
end
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
@array_list.push ' <testcase classname="' + @test_suite + '" name="' + test_name + '">'
|
||||
@array_list.push ' <failure type="ASSERT FAILED"> ' + reason + ' </failure>'
|
||||
@array_list.push ' </testcase>'
|
||||
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 detect_os
|
||||
os = RUBY_PLATFORM.split('-')
|
||||
@class_name = if os.size == 2
|
||||
if os[1] == 'mingw32'
|
||||
1
|
||||
else
|
||||
0
|
||||
end
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
# Main function used to parse the file that was captured.
|
||||
def process(name)
|
||||
@test_flag = false
|
||||
@array_list = []
|
||||
|
||||
detect_os
|
||||
|
||||
puts 'Parsing file: ' + name
|
||||
|
||||
test_pass = 0
|
||||
test_fail = 0
|
||||
test_ignore = 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)
|
||||
line_array = line.split(':')
|
||||
|
||||
# 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 (line_array.size >= 4) || (line.start_with? 'TEST(')
|
||||
# Determine if this test passed
|
||||
if line.include? ':PASS'
|
||||
test_passed(line_array)
|
||||
test_pass += 1
|
||||
elsif line.include? ':FAIL:'
|
||||
test_failed(line_array)
|
||||
test_fail += 1
|
||||
elsif line.include? ':IGNORE:'
|
||||
test_ignored(line_array)
|
||||
test_ignore += 1
|
||||
elsif line.start_with? 'TEST('
|
||||
if line.include? ' PASS'
|
||||
line_array = line.split(' ')
|
||||
test_passed_unity_fixture(line_array)
|
||||
test_pass += 1
|
||||
end
|
||||
# If none of the keywords are found there are no more tests for this suite so clear
|
||||
# the test flag
|
||||
else
|
||||
@test_flag = false
|
||||
end
|
||||
else
|
||||
@test_flag = false
|
||||
end
|
||||
end
|
||||
puts ''
|
||||
puts '=================== SUMMARY ====================='
|
||||
puts ''
|
||||
puts 'Tests Passed : ' + test_pass.to_s
|
||||
puts 'Tests Failed : ' + test_fail.to_s
|
||||
puts 'Tests Ignored : ' + test_ignore.to_s
|
||||
@total_tests = test_pass + test_fail + test_ignore
|
||||
|
||||
return unless @xml_out
|
||||
|
||||
heading = '<testsuite tests="' + @total_tests.to_s + '" failures="' + test_fail.to_s + '"' + ' skips="' + test_ignore.to_s + '">'
|
||||
@array_list.insert(0, heading)
|
||||
write_xml_output
|
||||
end
|
||||
end
|
||||
|
||||
# If the command line has no values in, used a default value of Output.txt
|
||||
parse_my_file = ParseOutput.new
|
||||
|
||||
if ARGV.size >= 1
|
||||
ARGV.each do |a|
|
||||
if a == '-xml'
|
||||
parse_my_file.set_xml_output
|
||||
else
|
||||
parse_my_file.process(a)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
252
tests/unity/auto/stylize_as_junit.rb
Normal file
252
tests/unity/auto/stylize_as_junit.rb
Normal file
@@ -0,0 +1,252 @@
|
||||
#!/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 |o|
|
||||
o.banner = 'Usage: unity_to_junit.rb [options]'
|
||||
|
||||
o.separator ''
|
||||
o.separator 'Specific options:'
|
||||
|
||||
o.on('-r', '--results <dir>', 'Look for Unity Results files here.') do |results|
|
||||
# puts "results #{results}"
|
||||
options.results_dir = results
|
||||
end
|
||||
|
||||
o.on('-p', '--root_path <path>', 'Prepend this path to files in results.') do |root_path|
|
||||
options.root_path = root_path
|
||||
end
|
||||
|
||||
o.on('-o', '--output <filename>', 'XML file to generate.') do |out_file|
|
||||
# puts "out_file: #{out_file}"
|
||||
options.out_file = out_file
|
||||
end
|
||||
|
||||
o.separator ''
|
||||
o.separator 'Common options:'
|
||||
|
||||
# No argument, shows at tail. This will print an options summary.
|
||||
o.on_tail('-h', '--help', 'Show this message') do
|
||||
puts o
|
||||
exit
|
||||
end
|
||||
|
||||
# Another typical switch to print the version.
|
||||
o.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
|
||||
attr_writer :targets, :root, :out_file
|
||||
|
||||
def initialize
|
||||
@report = ''
|
||||
@unit_name = ''
|
||||
end
|
||||
|
||||
def run
|
||||
# Clean up result file names
|
||||
results = @targets.map { |target| target.tr('\\', '/') }
|
||||
# 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(&:chomp)
|
||||
|
||||
raise "Empty test result file: #{result_file}" if lines.empty?
|
||||
|
||||
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])
|
||||
|
||||
# use line[0] from the test output to get the test_file path and name
|
||||
test_file_str = lines[0].tr('\\', '/')
|
||||
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 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 = results_structure
|
||||
lines.each do |line|
|
||||
line = line.tr('\\', '/')
|
||||
_src_file, src_line, test_name, status, msg = line.split(/:/)
|
||||
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
|
||||
results
|
||||
end
|
||||
|
||||
def parse_test_summary(summary)
|
||||
raise "Couldn't parse test results: #{summary}" unless summary.find { |v| v =~ /(\d+) Tests (\d+) Failures (\d+) Ignored/ }
|
||||
[Regexp.last_match(1).to_i, Regexp.last_match(2).to_i, Regexp.last_match(3).to_i]
|
||||
end
|
||||
|
||||
def here
|
||||
File.expand_path(File.dirname(__FILE__))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def results_structure
|
||||
{
|
||||
source: { path: '', file: '' },
|
||||
successes: [],
|
||||
failures: [],
|
||||
ignores: [],
|
||||
counts: { total: 0, passed: 0, failed: 0, ignored: 0 },
|
||||
stdout: []
|
||||
}
|
||||
end
|
||||
|
||||
def write_xml_header(stream)
|
||||
stream.puts "<?xml version='1.0' encoding='utf-8' ?>"
|
||||
end
|
||||
|
||||
def write_suites_header(stream)
|
||||
stream.puts '<testsuites>'
|
||||
end
|
||||
|
||||
def write_suite_header(counts, stream)
|
||||
stream.puts "\t<testsuite errors=\"0\" skipped=\"#{counts[:ignored]}\" failures=\"#{counts[:failed]}\" tests=\"#{counts[:total]}\" name=\"unity\">"
|
||||
end
|
||||
|
||||
def write_failures(results, stream)
|
||||
result = results[:failures]
|
||||
result.each do |item|
|
||||
filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
|
||||
stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
|
||||
stream.puts "\t\t\t<failure message=\"#{item[:message]}\" type=\"Assertion\"/>"
|
||||
stream.puts "\t\t\t<system-err>
[File] #{filename}
[Line] #{item[:line]}
</system-err>"
|
||||
stream.puts "\t\t</testcase>"
|
||||
end
|
||||
end
|
||||
|
||||
def write_tests(results, stream)
|
||||
result = results[:successes]
|
||||
result.each do |item|
|
||||
stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\" />"
|
||||
end
|
||||
end
|
||||
|
||||
def write_ignored(results, stream)
|
||||
result = results[:ignores]
|
||||
result.each do |item|
|
||||
filename = File.join(results[:source][:path], File.basename(results[:source][:file], '.*'))
|
||||
puts "Writing ignored tests for test harness: #{filename}"
|
||||
stream.puts "\t\t<testcase classname=\"#{@unit_name}\" name=\"#{item[:test]}\" time=\"0\">"
|
||||
stream.puts "\t\t\t<skipped message=\"#{item[:message]}\" type=\"Assertion\"/>"
|
||||
stream.puts "\t\t\t<system-err>
[File] #{filename}
[Line] #{item[:line]}
</system-err>"
|
||||
stream.puts "\t\t</testcase>"
|
||||
end
|
||||
end
|
||||
|
||||
def write_suite_footer(stream)
|
||||
stream.puts "\t</testsuite>"
|
||||
end
|
||||
|
||||
def write_suites_footer(stream)
|
||||
stream.puts '</testsuites>'
|
||||
end
|
||||
end # UnityToJUnit
|
||||
|
||||
if __FILE__ == $0
|
||||
# parse out the command options
|
||||
options = ArgvParser.parse(ARGV)
|
||||
|
||||
# create an instance to work with
|
||||
utj = UnityToJUnit.new
|
||||
begin
|
||||
# look in the specified or current directory for result files
|
||||
targets = "#{options.results_dir.tr('\\', '/')}**/*.test*"
|
||||
|
||||
results = Dir[targets]
|
||||
raise "No *.testpass, *.testfail, or *.testresults files found in '#{targets}'" if results.empty?
|
||||
utj.targets = results
|
||||
|
||||
# set the root path
|
||||
utj.root = options.root_path
|
||||
|
||||
# set the output XML file name
|
||||
# puts "Output File from options: #{options.out_file}"
|
||||
utj.out_file = options.out_file
|
||||
|
||||
# run the summarizer
|
||||
puts utj.run
|
||||
rescue StandardError => e
|
||||
utj.usage e.message
|
||||
end
|
||||
end
|
||||
25
tests/unity/auto/test_file_filter.rb
Normal file
25
tests/unity/auto/test_file_filter.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
# ==========================================
|
||||
# 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
|
||||
|
||||
return false unless @all_files
|
||||
return false unless File.exist?('test_file_filter.yml')
|
||||
|
||||
filters = YAML.load_file('test_file_filter.yml')
|
||||
@all_files = filters[:all_files]
|
||||
@only_files = filters[:only_files]
|
||||
@exclude_files = filters[:exclude_files]
|
||||
end
|
||||
|
||||
attr_accessor :all_files, :only_files, :exclude_files
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user