diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..befb2c20b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[Makefile] +indent_style = tab +indent_size = unset + +# ignore external repositories and test inputs +[tests/{unity,json-patch-tests,inputs}/*] +indent_style = unset +indent_size = unset +end_of_line = unset +charset = unset +trim_trailing_whitespace = unset +insert_final_newline = unset diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..883895fb1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto +/tests/inputs/* text eol=lf + +.gitattributes export-ignore +.gitignore export-ignore +.github export-ignore +.editorconfig export-ignore +.travis.yml export-ignore + +# Linguist incorrectly identified the headers as C++, manually override this. +*.h linguist-language=C diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 172cfcd3d..83d2e599f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -15,7 +15,7 @@ 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 +* Use 4 spaces 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=" "$(MIN_GCC_VERSION)") ifeq "$(IS_GCC_ABOVE_MIN_VERSION)" "1" CFLAGS += -fstack-protector-strong @@ -31,7 +36,8 @@ 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) +PIC_FLAGS = -fPIC +R_CFLAGS = $(PIC_FLAGS) -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') @@ -42,6 +48,8 @@ STATIC = a ## create dynamic (shared) library on Darwin (base OS for MacOSX and IOS) ifeq (Darwin, $(uname)) SHARED = dylib + CJSON_SO_LDFLAG = "" + UTILS_SO_LDFLAG = "" endif #cJSON library names @@ -66,11 +74,10 @@ shared: $(CJSON_SHARED) $(UTILS_SHARED) static: $(CJSON_STATIC) $(UTILS_STATIC) -tests: $(CJSON_TEST) $(UTILS_TEST) +tests: $(CJSON_TEST) test: tests ./$(CJSON_TEST) - ./$(UTILS_TEST) .c.o: $(CC) -c $(R_CFLAGS) $< @@ -91,16 +98,16 @@ $(UTILS_STATIC): $(UTILS_OBJ) #shared libraries .so.1.0.0 #cJSON $(CJSON_SHARED_VERSION): $(CJSON_OBJ) - $(CC) -shared -o $@ $< $(LDFLAGS) + $(CC) -shared -o $@ $< $(CJSON_SO_LDFLAG) $(LDFLAGS) #cJSON_Utils $(UTILS_SHARED_VERSION): $(UTILS_OBJ) - $(CC) -shared -o $@ $< $(LDFLAGS) + $(CC) -shared -o $@ $< $(CJSON_OBJ) $(UTILS_SO_LDFLAG) $(LDFLAGS) #objects #cJSON $(CJSON_OBJ): cJSON.c cJSON.h #cJSON_Utils -$(UTILS_OBJ): cJSON_Utils.c cJSON_Utils.h +$(UTILS_OBJ): cJSON_Utils.c cJSON_Utils.h cJSON.h #links .so -> .so.1 -> .so.1.0.0 @@ -134,9 +141,8 @@ uninstall-cjson: uninstall-utils $(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED) $(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED_VERSION) $(RM) $(INSTALL_LIBRARY_PATH)/$(CJSON_SHARED_SO) - rmdir $(INSTALL_LIBRARY_PATH) $(RM) $(INSTALL_INCLUDE_PATH)/cJSON.h - rmdir $(INSTALL_INCLUDE_PATH) + #cJSON_Utils uninstall-utils: $(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED) @@ -144,10 +150,14 @@ uninstall-utils: $(RM) $(INSTALL_LIBRARY_PATH)/$(UTILS_SHARED_SO) $(RM) $(INSTALL_INCLUDE_PATH)/cJSON_Utils.h -uninstall: uninstall-utils uninstall-cjson +remove-dir: + $(if $(wildcard $(INSTALL_LIBRARY_PATH)/*.*),,rmdir $(INSTALL_LIBRARY_PATH)) + $(if $(wildcard $(INSTALL_INCLUDE_PATH)/*.*),,rmdir $(INSTALL_INCLUDE_PATH)) + +uninstall: uninstall-utils uninstall-cjson remove-dir clean: $(RM) $(CJSON_OBJ) $(UTILS_OBJ) #delete object files $(RM) $(CJSON_SHARED) $(CJSON_SHARED_VERSION) $(CJSON_SHARED_SO) $(CJSON_STATIC) #delete cJSON $(RM) $(UTILS_SHARED) $(UTILS_SHARED_VERSION) $(UTILS_SHARED_SO) $(UTILS_STATIC) #delete cJSON_Utils - $(RM) $(CJSON_TEST) $(UTILS_TEST) #delete tests + $(RM) $(CJSON_TEST) #delete test diff --git a/README.md b/README.md index 4dba514ac..9c7ed142c 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,31 @@ Ultralightweight JSON parser in ANSI C. * [Usage](#usage) * [Welcome to cJSON](#welcome-to-cjson) * [Building](#building) - * [Some JSON](#some-json) - * [Here's the structure](#heres-the-structure) + * [Copying the source](#copying-the-source) + * [CMake](#cmake) + * [Makefile](#makefile) + * [Meson](#meson) + * [Vcpkg](#Vcpkg) + * [Including cJSON](#including-cjson) + * [Data Structure](#data-structure) + * [Working with the data structure](#working-with-the-data-structure) + * [Basic types](#basic-types) + * [Arrays](#arrays) + * [Objects](#objects) + * [Parsing JSON](#parsing-json) + * [Printing JSON](#printing-json) + * [Example](#example) + * [Printing](#printing) + * [Parsing](#parsing) * [Caveats](#caveats) + * [Zero Character](#zero-character) + * [Character Encoding](#character-encoding) + * [C Standard](#c-standard) + * [Floating Point Numbers](#floating-point-numbers) + * [Deep Nesting Of Arrays And Objects](#deep-nesting-of-arrays-and-objects) + * [Thread Safety](#thread-safety) + * [Case Sensitivity](#case-sensitivity) + * [Duplicate Object Members](#duplicate-object-members) * [Enjoy cJSON!](#enjoy-cjson) ## License @@ -60,12 +82,14 @@ philosophy as JSON itself. Simple, dumb, out of the way. There are several ways to incorporate cJSON into your project. #### copying the source + Because the entire library is only one C file and one header file, you can just copy `cJSON.h` and `cJSON.c` to your projects source and start using it. 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. 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. + +With CMake, cJSON supports a full blown build system. This way you get the most features. CMake with an equal or higher version than 3.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 @@ -82,14 +106,20 @@ 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_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_CUSTOM_COMPILER_FLAGS=On`: Enable custom compiler flags (currently for Clang, GCC and MSVC). 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) +* `-DENABLE_SAFE_STACK`: Enable the [SafeStack](https://clang.llvm.org/docs/SafeStack.html) instrumentation pass. Currently only works with the Clang compiler. (off by default) * `-DBUILD_SHARED_LIBS=On`: Build the shared libraries. (on by default) +* `-DBUILD_SHARED_AND_STATIC_LIBS=On`: Build both shared and static libraries. (off by default) * `-DCMAKE_INSTALL_PREFIX=/usr`: Set a prefix for the installation. +* `-DENABLE_LOCALES=On`: Enable the usage of localeconv method. ( on by default ) +* `-DCJSON_OVERRIDE_BUILD_SHARED_LIBS=On`: Enable overriding the value of `BUILD_SHARED_LIBS` with `-DCJSON_BUILD_SHARED_LIBS`. +* `-DENABLE_CJSON_VERSION_SO`: Enable cJSON so version. ( on by default ) If you are packaging cJSON for a distribution of Linux, you would probably take these steps for example: ``` @@ -100,280 +130,414 @@ make make DESTDIR=$pkgdir install ``` +On Windows CMake is usually used to create a Visual Studio solution file by running it inside the Developer Command Prompt for Visual Studio, for exact steps follow the official documentation from CMake and Microsoft and use the online search engine of your choice. The descriptions of the the options above still generally apply, although not all of them work on Windows. + #### Makefile + +**NOTE:** This Method is deprecated. Use CMake if at all possible. Makefile support is limited to fixing bugs. + 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. +Run this command in the directory with the source code and it will automatically compile static and shared libraries and a little test program (not the full test suite). ``` make all ``` -If you want, you can install the compiled library to your system using `make install`. By default it will install the headers in `/usr/local/include/cjson` and the libraries in `/usr/local/lib`. But you can change this behavior by setting the `PREFIX` and `DESTDIR` variables: `make PREFIX=/usr DESTDIR=temp install`. +If you want, you can install the compiled library to your system using `make install`. By default it will install the headers in `/usr/local/include/cjson` and the libraries in `/usr/local/lib`. But you can change this behavior by setting the `PREFIX` and `DESTDIR` variables: `make PREFIX=/usr DESTDIR=temp install`. And uninstall them with: `make PREFIX=/usr DESTDIR=temp uninstall`. -### Some JSON: +#### Meson -```json -{ - "name": "Jack (\"Bee\") Nimble", - "format": { - "type": "rect", - "width": 1920, - "height": 1080, - "interlace": false, - "frame rate": 24 - } -} -``` +To make cjson work in a project using meson, the libcjson dependency has to be included: -Assume that you got this from a file, a webserver, or magic JSON elves, whatever, -you have a `char *` to it. Everything is a `cJSON` struct. -Get it parsed: +```meson +project('c-json-example', 'c') -```c -cJSON * root = cJSON_Parse(my_json_string); +cjson = dependency('libcjson') + +example = executable( + 'example', + 'example.c', + dependencies: [cjson], +) ``` -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_GetObjectItemCaseSensitive(root, "format"); -cJSON *framerate_item = cJSON_GetObjectItemCaseSensitive(format, "frame rate"); -double framerate = 0; -if (cJSON_IsNumber(framerate_item)) -{ - framerate = framerate_item->valuedouble; -} +#### Vcpkg + +You can download and install cJSON using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: ``` +git clone https://github.com/Microsoft/vcpkg.git +cd vcpkg +./bootstrap-vcpkg.sh +./vcpkg integrate install +vcpkg install cjson +``` + +The cJSON port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. -Want to change the framerate? +### Including cJSON + +If you installed it via CMake or the Makefile, you can include cJSON like this: ```c -cJSON *framerate_item = cJSON_GetObjectItemCaseSensitive(format, "frame rate"); -cJSON_SetNumberValue(framerate_item, 25); +#include ``` -Back to disk? +### Data Structure + +cJSON represents JSON data using the `cJSON` struct data type: ```c -char *rendered = cJSON_Print(root); +/* The cJSON structure: */ +typedef struct cJSON +{ + struct cJSON *next; + struct cJSON *prev; + struct cJSON *child; + int type; + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + double valuedouble; + char *string; +} cJSON; ``` -Finished? Delete the root (this takes care of everything else). +An item of this type represents a JSON value. The type is stored in `type` as a bit-flag (**this means that you cannot find out the type by just comparing the value of `type`**). + +To check the type of an item, use the corresponding `cJSON_Is...` function. It does a `NULL` check followed by a type check and returns a boolean value if the item is of this type. + +The type can be one of the following: + +* `cJSON_Invalid` (check with `cJSON_IsInvalid`): Represents an invalid item that doesn't contain any value. You automatically have this type if you set the item to all zero bytes. +* `cJSON_False` (check with `cJSON_IsFalse`): Represents a `false` boolean value. You can also check for boolean values in general with `cJSON_IsBool`. +* `cJSON_True` (check with `cJSON_IsTrue`): Represents a `true` boolean value. You can also check for boolean values in general with `cJSON_IsBool`. +* `cJSON_NULL` (check with `cJSON_IsNull`): Represents a `null` value. +* `cJSON_Number` (check with `cJSON_IsNumber`): Represents a number value. The value is stored as a double in `valuedouble` and also in `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint`. +* `cJSON_String` (check with `cJSON_IsString`): Represents a string value. It is stored in the form of a zero terminated string in `valuestring`. +* `cJSON_Array` (check with `cJSON_IsArray`): Represent an array value. This is implemented by pointing `child` to a linked list of `cJSON` items that represent the values in the array. The elements are linked together using `next` and `prev`, where the first element has `prev.next == NULL` and the last element `next == NULL`. +* `cJSON_Object` (check with `cJSON_IsObject`): Represents an object value. Objects are stored same way as an array, the only difference is that the items in the object store their keys in `string`. +* `cJSON_Raw` (check with `cJSON_IsRaw`): Represents any kind of JSON that is stored as a zero terminated array of characters in `valuestring`. This can be used, for example, to avoid printing the same static JSON over and over again to save performance. cJSON will never create this type when parsing. Also note that cJSON doesn't check if it is valid JSON. + +Additionally there are the following two flags: + +* `cJSON_IsReference`: Specifies that the item that `child` points to and/or `valuestring` is not owned by this item, it is only a reference. So `cJSON_Delete` and other functions will only deallocate this item, not its `child`/`valuestring`. +* `cJSON_StringIsConst`: This means that `string` points to a constant string. This means that `cJSON_Delete` and other functions will not try to deallocate `string`. + +### Working with the data structure + +For every value type there is a `cJSON_Create...` function that can be used to create an item of that type. +All of these will allocate a `cJSON` struct that can later be deleted with `cJSON_Delete`. +Note that you have to delete them at some point, otherwise you will get a memory leak. +**Important**: If you have added an item to an array or an object already, you **mustn't** delete it with `cJSON_Delete`. Adding it to an array or object transfers its ownership so that when that array or object is deleted, +it gets deleted as well. You also could use `cJSON_SetValuestring` to change a `cJSON_String`'s `valuestring`, and you needn't to free the previous `valuestring` manually. + +#### Basic types + +* **null** is created with `cJSON_CreateNull` +* **booleans** are created with `cJSON_CreateTrue`, `cJSON_CreateFalse` or `cJSON_CreateBool` +* **numbers** are created with `cJSON_CreateNumber`. This will set both `valuedouble` and `valueint`. If the number is outside of the range of an integer, `INT_MAX` or `INT_MIN` are used for `valueint` +* **strings** are created with `cJSON_CreateString` (copies the string) or with `cJSON_CreateStringReference` (directly points to the string. This means that `valuestring` won't be deleted by `cJSON_Delete` and you are responsible for its lifetime, useful for constants) + +#### Arrays + +You can create an empty array with `cJSON_CreateArray`. `cJSON_CreateArrayReference` can be used to create an array that doesn't "own" its content, so its content doesn't get deleted by `cJSON_Delete`. + +To add items to an array, use `cJSON_AddItemToArray` to append items to the end. +Using `cJSON_AddItemReferenceToArray` an element can be added as a reference to another item, array or string. This means that `cJSON_Delete` will not delete that items `child` or `valuestring` properties, so no double frees are occurring if they are already used elsewhere. +To insert items in the middle, use `cJSON_InsertItemInArray`. It will insert an item at the given 0 based index and shift all the existing items to the right. + +If you want to take an item out of an array at a given index and continue using it, use `cJSON_DetachItemFromArray`, it will return the detached item, so be sure to assign it to a pointer, otherwise you will have a memory leak. + +Deleting items is done with `cJSON_DeleteItemFromArray`. It works like `cJSON_DetachItemFromArray`, but deletes the detached item via `cJSON_Delete`. + +You can also replace an item in an array in place. Either with `cJSON_ReplaceItemInArray` using an index or with `cJSON_ReplaceItemViaPointer` given a pointer to an element. `cJSON_ReplaceItemViaPointer` will return `0` if it fails. What this does internally is to detach the old item, delete it and insert the new item in its place. + +To get the size of an array, use `cJSON_GetArraySize`. Use `cJSON_GetArrayItem` to get an element at a given index. + +Because an array is stored as a linked list, iterating it via index is inefficient (`O(n²)`), so you can iterate over an array using the `cJSON_ArrayForEach` macro in `O(n)` time complexity. + +#### Objects + +You can create an empty object with `cJSON_CreateObject`. `cJSON_CreateObjectReference` can be used to create an object that doesn't "own" its content, so its content doesn't get deleted by `cJSON_Delete`. + +To add items to an object, use `cJSON_AddItemToObject`. Use `cJSON_AddItemToObjectCS` to add an item to an object with a name that is a constant or reference (key of the item, `string` in the `cJSON` struct), so that it doesn't get freed by `cJSON_Delete`. +Using `cJSON_AddItemReferenceToArray` an element can be added as a reference to another object, array or string. This means that `cJSON_Delete` will not delete that items `child` or `valuestring` properties, so no double frees are occurring if they are already used elsewhere. + +If you want to take an item out of an object, use `cJSON_DetachItemFromObjectCaseSensitive`, it will return the detached item, so be sure to assign it to a pointer, otherwise you will have a memory leak. + +Deleting items is done with `cJSON_DeleteItemFromObjectCaseSensitive`. It works like `cJSON_DetachItemFromObjectCaseSensitive` followed by `cJSON_Delete`. + +You can also replace an item in an object in place. Either with `cJSON_ReplaceItemInObjectCaseSensitive` using a key or with `cJSON_ReplaceItemViaPointer` given a pointer to an element. `cJSON_ReplaceItemViaPointer` will return `0` if it fails. What this does internally is to detach the old item, delete it and insert the new item in its place. + +To get the size of an object, you can use `cJSON_GetArraySize`, this works because internally objects are stored as arrays. + +If you want to access an item in an object, use `cJSON_GetObjectItemCaseSensitive`. + +To iterate over an object, you can use the `cJSON_ArrayForEach` macro the same way as for arrays. + +cJSON also provides convenient helper functions for quickly creating a new item and adding it to an object, like `cJSON_AddNullToObject`. They return a pointer to the new item or `NULL` if they failed. + +### Parsing JSON + +Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse`. ```c -cJSON_Delete(root); +cJSON *json = cJSON_Parse(string); ``` -That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers -before you dereference them. If you want to see how you'd build this struct in code? +Given some JSON in a string (whether zero terminated or not), you can parse it with `cJSON_ParseWithLength`. ```c -cJSON *root; -cJSON *fmt; -root = cJSON_CreateObject(); -cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble")); -cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject()); -cJSON_AddStringToObject(fmt, "type", "rect"); -cJSON_AddNumberToObject(fmt, "width", 1920); -cJSON_AddNumberToObject(fmt, "height", 1080); -cJSON_AddFalseToObject (fmt, "interlace"); -cJSON_AddNumberToObject(fmt, "frame rate", 24); +cJSON *json = cJSON_ParseWithLength(string, buffer_length); ``` -Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup. -Look at `test.c` for a bunch of nice examples, mostly all ripped off the [json.org](http://json.org) site, and -a few from elsewhere. +It will parse the JSON and allocate a tree of `cJSON` items that represents it. Once it returns, you are fully responsible for deallocating it after use with `cJSON_Delete`. -What about manual mode? First up you need some detail. -Let's cover how the `cJSON` objects represent the JSON data. -cJSON doesn't distinguish arrays from objects in handling; just type. -Each `cJSON` has, potentially, a child, siblings, value, a name. +The allocator used by `cJSON_Parse` is `malloc` and `free` by default but can be changed (globally) with `cJSON_InitHooks`. -* The `root` object has: *Object* Type and a Child -* The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling: -* Sibling has type *Object*, name "format", and a child. -* That child has type *String*, name "type", value "rect", and a sibling: -* Sibling has type *Number*, name "width", value 1920, and a sibling: -* Sibling has type *Number*, name "height", value 1080, and a sibling: -* Sibling has type *False*, name "interlace", and a sibling: -* Sibling has type *Number*, name "frame rate", value 24 +If an error occurs a pointer to the position of the error in the input string can be accessed using `cJSON_GetErrorPtr`. Note though that this can produce race conditions in multithreading scenarios, in that case it is better to use `cJSON_ParseWithOpts` with `return_parse_end`. +By default, characters in the input string that follow the parsed JSON will not be considered as an error. -### Here's the structure: +If you want more options, use `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`. +`return_parse_end` returns a pointer to the end of the JSON in the input string or the position that an error occurs at (thereby replacing `cJSON_GetErrorPtr` in a thread safe way). `require_null_terminated`, if set to `1` will make it an error if the input string contains data after the JSON. -```c -typedef struct cJSON { - struct cJSON *next,*prev; - struct cJSON *child; +If you want more options giving buffer length, use `cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)`. - int type; +### Printing JSON - char *valuestring; - int valueint; /* writing to valueint is DEPRECATED, please use cJSON_SetNumberValue instead */ - double valuedouble; +Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`. - char *string; -} cJSON; +```c +char *string = cJSON_Print(json); ``` -By default all values are 0 unless set by virtue of being meaningful. +It will allocate a string and print a JSON representation of the tree into it. Once it returns, you are fully responsible for deallocating it after use with your allocator. (usually `free`, depends on what has been set with `cJSON_InitHooks`). -`next`/`prev` is a doubly linked list of siblings. `next` takes you to your sibling, -`prev` takes you back from your sibling to you. -Only objects and arrays have a `child`, and it's the head of the doubly linked list. -A `child` entry will have `prev == 0`, but next potentially points on. The last sibling has `next == 0`. -The type expresses *Null*/*True*/*False*/*Number*/*String*/*Array*/*Object*, all of which are `#defined` in -`cJSON.h`. +`cJSON_Print` will print with whitespace for formatting. If you want to print without formatting, use `cJSON_PrintUnformatted`. -A *Number* has `valueint` and `valuedouble`. `valueint` is a relict of the past, so always use `valuedouble`. +If you have a rough idea of how big your resulting string will be, you can use `cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)`. `fmt` is a boolean to turn formatting with whitespace on and off. `prebuffer` specifies the first buffer size to use for printing. `cJSON_Print` currently uses 256 bytes for its first buffer size. Once printing runs out of space, a new buffer is allocated and the old gets copied over before printing is continued. -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`. -`string` is the JSON name for the 'variable name' if you will. +These dynamic buffer allocations can be completely avoided by using `cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)`. It takes a buffer to a pointer to print to and its length. If the length is reached, printing will fail and it returns `0`. In case of success, `1` is returned. Note that you should provide 5 bytes more than is actually needed, because cJSON is not 100% accurate in estimating if the provided memory is enough. -Now you can trivially walk the lists, recursively, and parse as you please. -You can invoke `cJSON_Parse` to get cJSON to parse for you, and then you can take -the root object, and traverse the structure (which is, formally, an N-tree), -and tokenise as you please. If you wanted to build a callback style parser, this is how -you'd do it (just an example, since these things are very specific): +### Example -```c -void parse_and_callback(cJSON *item, const char *prefix) +In this example we want to build and parse the following JSON: + +```json { - while (item) - { - 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) + "name": "Awesome 4K", + "resolutions": [ + { + "width": 1280, + "height": 720 + }, + { + "width": 1920, + "height": 1080 + }, { - parse_and_callback(item->child, newprefix); + "width": 3840, + "height": 2160 } - item = item->next; - free(newprefix); - } + ] } ``` -The `prefix` process will build you a separated list, to simplify your callback handling. -The `dorecurse` flag would let the callback decide to handle sub-arrays on it's own, or -let you invoke it per-item. For the item above, your callback might look like this: +#### Printing + +Let's build the above JSON and print it to a string: ```c -int callback(const char *name, int type, cJSON *item) +//create a monitor with a list of supported resolutions +//NOTE: Returns a heap allocated string, you are required to free it after use. +char *create_monitor(void) { - if (!strcmp(name, "name")) + const unsigned int resolution_numbers[3][2] = { + {1280, 720}, + {1920, 1080}, + {3840, 2160} + }; + char *string = NULL; + cJSON *name = NULL; + cJSON *resolutions = NULL; + cJSON *resolution = NULL; + cJSON *width = NULL; + cJSON *height = NULL; + size_t index = 0; + + cJSON *monitor = cJSON_CreateObject(); + if (monitor == NULL) { - /* populate name */ + goto end; } - else if (!strcmp(name, "format/type")) - { - /* handle "rect" */ } - else if (!strcmp(name, "format/width")) + + name = cJSON_CreateString("Awesome 4K"); + if (name == NULL) { - /* 800 */ + goto end; } - else if (!strcmp(name, "format/height")) + /* after creation was successful, immediately add it to the monitor, + * thereby transferring ownership of the pointer to it */ + cJSON_AddItemToObject(monitor, "name", name); + + resolutions = cJSON_CreateArray(); + if (resolutions == NULL) { - /* 600 */ + goto end; } - else if (!strcmp(name, "format/interlace")) + cJSON_AddItemToObject(monitor, "resolutions", resolutions); + + for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) { - /* false */ + resolution = cJSON_CreateObject(); + if (resolution == NULL) + { + goto end; + } + cJSON_AddItemToArray(resolutions, resolution); + + width = cJSON_CreateNumber(resolution_numbers[index][0]); + if (width == NULL) + { + goto end; + } + cJSON_AddItemToObject(resolution, "width", width); + + height = cJSON_CreateNumber(resolution_numbers[index][1]); + if (height == NULL) + { + goto end; + } + cJSON_AddItemToObject(resolution, "height", height); } - else if (!strcmp(name, "format/frame rate")) + + string = cJSON_Print(monitor); + if (string == NULL) { - /* 24 */ + fprintf(stderr, "Failed to print monitor.\n"); } - return 1; +end: + cJSON_Delete(monitor); + return string; } ``` -Alternatively, you might like to parse iteratively. -You'd use: +Alternatively we can use the `cJSON_Add...ToObject` helper functions to make our lives a little easier: ```c -void parse_object(cJSON *item) +//NOTE: Returns a heap allocated string, you are required to free it after use. +char *create_monitor_with_helpers(void) { - int i; - for (i = 0; i < cJSON_GetArraySize(item); i++) + const unsigned int resolution_numbers[3][2] = { + {1280, 720}, + {1920, 1080}, + {3840, 2160} + }; + char *string = NULL; + cJSON *resolutions = NULL; + size_t index = 0; + + cJSON *monitor = cJSON_CreateObject(); + + if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL) { - cJSON *subitem = cJSON_GetArrayItem(item, i); - // handle subitem + goto end; } -} -``` -Or, for PROPER manual mode: + resolutions = cJSON_AddArrayToObject(monitor, "resolutions"); + if (resolutions == NULL) + { + goto end; + } -```c -void parse_object(cJSON *item) -{ - cJSON *subitem = item->child; - while (subitem) + for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) { - // handle subitem - if (subitem->child) + cJSON *resolution = cJSON_CreateObject(); + + if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL) + { + goto end; + } + + if (cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL) { - parse_object(subitem->child); + goto end; } - subitem = subitem->next; + cJSON_AddItemToArray(resolutions, resolution); } + + string = cJSON_Print(monitor); + if (string == NULL) + { + fprintf(stderr, "Failed to print monitor.\n"); + } + +end: + cJSON_Delete(monitor); + return string; } ``` -Of course, this should look familiar, since this is just a stripped-down version -of the callback-parser. +#### Parsing -This should cover most uses you'll find for parsing. The rest should be possible -to infer.. and if in doubt, read the source! There's not a lot of it! ;) - -In terms of constructing JSON data, the example code above is the right way to do it. -You can, of course, hand your sub-objects to other functions to populate. -Also, if you find a use for it, you can manually build the objects. -For instance, suppose you wanted to build an array of objects? +In this example we will parse a JSON in the above format and check if the monitor supports a Full HD resolution while printing some diagnostic output: ```c -cJSON *objects[24]; - -cJSON *Create_array_of_anything(cJSON **items, int num) +/* return 1 if the monitor supports full hd, 0 otherwise */ +int supports_full_hd(const char * const monitor) { - int i; - cJSON *prev; - cJSON *root = cJSON_CreateArray(); - for (i = 0; i < 24; i++) + const cJSON *resolution = NULL; + const cJSON *resolutions = NULL; + const cJSON *name = NULL; + int status = 0; + cJSON *monitor_json = cJSON_Parse(monitor); + if (monitor_json == NULL) { - if (!i) + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) { - root->child = objects[i]; + fprintf(stderr, "Error before: %s\n", error_ptr); } - else + status = 0; + goto end; + } + + name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name"); + if (cJSON_IsString(name) && (name->valuestring != NULL)) + { + printf("Checking monitor \"%s\"\n", name->valuestring); + } + + resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions"); + cJSON_ArrayForEach(resolution, resolutions) + { + cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width"); + cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height"); + + if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height)) { - prev->next = objects[i]; - objects[i]->prev = prev; + status = 0; + goto end; } - prev = objects[i]; + if ((width->valuedouble == 1920) && (height->valuedouble == 1080)) + { + status = 1; + goto end; + } } - return root; +end: + cJSON_Delete(monitor_json); + return status; } ``` -and simply: `Create_array_of_anything(objects, 24);` - -cJSON doesn't make any assumptions about what order you create things in. -You can attach the objects, as above, and later add children to each -of those objects. - -As soon as you call `cJSON_Print`, it renders the structure to text. - -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[]`. +Note that there are no NULL checks except for the result of `cJSON_Parse` because `cJSON_GetObjectItemCaseSensitive` checks for `NULL` inputs already, so a `NULL` value is just propagated and `cJSON_IsNumber` and `cJSON_IsString` return `0` if the input is `NULL`. ### Caveats @@ -406,6 +570,7 @@ cJSON doesn't support arrays and objects that are nested too deeply because this 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. @@ -414,7 +579,12 @@ However it is thread safe under the following conditions: 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. +#### Duplicate Object Members + +cJSON supports parsing and printing JSON that contains objects that have multiple members with the same name. `cJSON_GetObjectItemCaseSensitive` however will always only return the first one. + # Enjoy cJSON! -- Dave Gamble, Aug 2009 -- [cJSON contributors](CONTRIBUTORS.md) +- Dave Gamble (original author) +- Max Bruckner and Alan Wang (current maintainer) +- and the other [cJSON contributors](CONTRIBUTORS.md) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..33d99a2e9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +# Security Policy + +## Supported Versions + +Security is of the highest importance and all security vulnerabilities or suspected security vulnerabilities should be reported to cjson team privately, to minimize attacks against current users of cjson before they are fixed. Vulnerabilities will be investigated and patched on the next patch (or minor) release as soon as possible. This information could be kept entirely internal to the project. + +## Reporting a Vulnerability + +If you know of a publicly disclosed security vulnerability for cjson, please IMMEDIATELY contact wp_scut@163.com and peterlee@apache.org to inform the cjson Team. + +IMPORTANT: Do not file public issues on GitHub for security vulnerabilities. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..464bf0388 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,86 @@ +os: Visual Studio 2015 + +# ENABLE_CUSTOM_COMPILER_FLAGS - on by default +# ENABLE_SANITIZERS - off by default +# ENABLE_PUBLIC_SYMBOLS - on by default +# BUILD_SHARED_LIBS - on by default +# ENABLE_TARGET_EXPORT - on by default +# ENABLE_CJSON_UTILS - off by default +# ENABLE_CJSON_TEST -on by default +# ENABLE_VALGRIND - off by default +# ENABLE_FUZZING - off by default + +environment: + matrix: + - GENERATOR: "Visual Studio 14 2015" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 14 2015" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 12 2013" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 12 2013" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 11 2012" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 11 2012" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 10 2010" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 10 2010" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 9 2008" + BUILD_SHARED_LIBS: ON + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + - GENERATOR: "Visual Studio 9 2008" + BUILD_SHARED_LIBS: OFF + ENABLE_CJSON_TEST: OFF + ENABLE_CJSON_UTILS: ON + + +platform: + - x86 + - x64 +matrix: + exclude: + - platform: x64 + GENERATOR: "Visual Studio 9 2008" + +configuration: + - Release + + +build_script: + - ps: if($env:PLATFORM -eq "x64") { $env:CMAKE_GEN_SUFFIX=" Win64" } + - cmake "-G%GENERATOR%%CMAKE_GEN_SUFFIX%" -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% -DENABLE_CJSON_TEST=%ENABLE_CJSON_TEST% -H. -Bbuild + - cmake --build build --config "%CONFIGURATION%" + + +on_failure: + - ps: if(Test-Path builds/CMakeFiles/CMakeOutput.log) { cat builds/CMakeFiles/CMakeOutput.log } + - ps: if(Test-Path builds/CMakeFiles/CMakeError.log) { cat builds/CMakeFiles/CMakeError.log } \ No newline at end of file diff --git a/cJSON.c b/cJSON.c index 2494cd965..88c2d95b3 100644 --- a/cJSON.c +++ b/cJSON.c @@ -23,19 +23,35 @@ /* cJSON */ /* JSON parser in C. */ +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + #ifdef __GNUC__ #pragma GCC visibility push(default) #endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif #include #include #include #include -#include #include #include +#include + +#ifdef ENABLE_LOCALES #include +#endif +#if defined(_MSC_VER) +#pragma warning (pop) +#endif #ifdef __GNUC__ #pragma GCC visibility pop #endif @@ -43,9 +59,32 @@ #include "cJSON.h" /* define our own boolean type */ +#ifdef true +#undef true +#endif #define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif #define false ((cJSON_bool)0) +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0/0.0 +#endif +#endif + typedef struct { const unsigned char *json; size_t position; @@ -57,8 +96,28 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) return (const char*) (global_error.json + global_error.position); } +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ -#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 5) || (CJSON_VERSION_PATCH != 3) +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 19) #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. #endif @@ -96,12 +155,35 @@ static int case_insensitive_strcmp(const unsigned char *string1, const unsigned typedef struct internal_hooks { - void *(*allocate)(size_t size); - void (*deallocate)(void *pointer); - void *(*reallocate)(void *pointer, size_t size); + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); } internal_hooks; -static internal_hooks global_hooks = { malloc, free, realloc }; +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) { @@ -114,7 +196,8 @@ static unsigned char* cJSON_strdup(const unsigned char* string, const internal_h } length = strlen((const char*)string) + sizeof(""); - if (!(copy = (unsigned char*)hooks->allocate(length))) + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) { return NULL; } @@ -180,10 +263,12 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { global_hooks.deallocate(item->valuestring); + item->valuestring = NULL; } if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { global_hooks.deallocate(item->string); + item->string = NULL; } global_hooks.deallocate(item); item = next; @@ -193,8 +278,12 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) /* get the decimal point character of the current locale */ static unsigned char get_decimal_point(void) { +#ifdef ENABLE_LOCALES struct lconv *lconv = localeconv(); return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif } typedef struct @@ -208,7 +297,6 @@ typedef struct /* check if the given size is left to read in a given parse buffer (starting with 1) */ #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) -#define cannot_read(buffer, size) (!can_read(buffer, size)) /* check if the buffer can be accessed at the given index (starting with 0) */ #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) @@ -220,9 +308,11 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu { double number = 0; unsigned char *after_end = NULL; - unsigned char number_c_string[64]; + unsigned char *number_c_string; unsigned char decimal_point = get_decimal_point(); size_t i = 0; + size_t number_string_length = 0; + cJSON_bool has_decimal_point = false; if ((input_buffer == NULL) || (input_buffer->content == NULL)) { @@ -232,7 +322,7 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu /* copy the number into a temporary buffer and replace '.' with the decimal point * of the current locale (for strtod) * This also takes care of '\0' not necessarily being available for marking the end of the input */ - for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + for (i = 0; can_access_at_index(input_buffer, i); i++) { switch (buffer_at_offset(input_buffer)[i]) { @@ -250,11 +340,12 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu case '-': case 'e': case 'E': - number_c_string[i] = buffer_at_offset(input_buffer)[i]; + number_string_length++; break; case '.': - number_c_string[i] = decimal_point; + number_string_length++; + has_decimal_point = true; break; default: @@ -262,11 +353,33 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu } } loop_end: - number_c_string[i] = '\0'; + /* malloc for temporary buffer, add 1 for '\0' */ + number_c_string = (unsigned char *) input_buffer->hooks.allocate(number_string_length + 1); + if (number_c_string == NULL) + { + return false; /* allocation failure */ + } + + memcpy(number_c_string, buffer_at_offset(input_buffer), number_string_length); + number_c_string[number_string_length] = '\0'; + + if (has_decimal_point) + { + for (i = 0; i < number_string_length; i++) + { + if (number_c_string[i] == '.') + { + /* replace '.' with the decimal point of the current locale (for strtod) */ + number_c_string[i] = decimal_point; + } + } + } number = strtod((const char*)number_c_string, (char**)&after_end); if (number_c_string == after_end) { + /* free the temporary buffer */ + input_buffer->hooks.deallocate(number_c_string); return false; /* parse_error */ } @@ -277,7 +390,7 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu { item->valueint = INT_MAX; } - else if (number <= INT_MIN) + else if (number <= (double)INT_MIN) { item->valueint = INT_MIN; } @@ -289,17 +402,24 @@ static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_bu item->type = cJSON_Number; input_buffer->offset += (size_t)(after_end - number_c_string); + /* free the temporary buffer */ + input_buffer->hooks.deallocate(number_c_string); return true; } /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) { + if (object == NULL) + { + return (double)NAN; + } + if (number >= INT_MAX) { object->valueint = INT_MAX; } - else if (number <= INT_MIN) + else if (number <= (double)INT_MIN) { object->valueint = INT_MIN; } @@ -311,6 +431,50 @@ CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) return object->valuedouble = number; } +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + size_t v1_len; + size_t v2_len; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) + { + return NULL; + } + + v1_len = strlen(valuestring); + v2_len = strlen(object->valuestring); + + if (v1_len <= v2_len) + { + /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */ + if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring )) + { + return NULL; + } + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + typedef struct { unsigned char *buffer; @@ -377,6 +541,14 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) { /* reallocate with realloc if available */ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } } else { @@ -390,10 +562,8 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) return NULL; } - if (newbuffer) - { - memcpy(newbuffer, p->buffer, p->offset + 1); - } + + memcpy(newbuffer, p->buffer, p->offset + 1); p->hooks.deallocate(p->buffer); } p->length = newsize; @@ -415,6 +585,13 @@ static void update_offset(printbuffer * const buffer) buffer->offset += strlen((const char*)buffer_pointer); } +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + /* Render the number nicely from the given item into a string. */ static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { @@ -422,9 +599,9 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out double d = item->valuedouble; int length = 0; size_t i = 0; - unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ unsigned char decimal_point = get_decimal_point(); - double test; + double test = 0.0; if (output_buffer == NULL) { @@ -432,31 +609,35 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out } /* This checks for NaN and Infinity */ - if ((d * 0) != 0) + if (isnan(d) || isinf(d)) { length = sprintf((char*)number_buffer, "null"); } + else if(d == (double)item->valueint) + { + length = sprintf((char*)number_buffer, "%d", item->valueint); + } else { /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ length = sprintf((char*)number_buffer, "%1.15g", d); /* Check whether the original double can be recovered */ - if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) { /* If not, print with 17 decimal places of precision */ length = sprintf((char*)number_buffer, "%1.17g", d); } } - /* sprintf failed or buffer overrun occured */ + /* sprintf failed or buffer overrun occurred */ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) { return false; } /* reserve appropriate space in the output */ - output_pointer = ensure(output_buffer, (size_t)length); + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); if (output_pointer == NULL) { return false; @@ -758,6 +939,7 @@ static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_bu if (output != NULL) { input_buffer->hooks.deallocate(output); + output = NULL; } if (input_pointer != NULL) @@ -912,6 +1094,11 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) return NULL; } + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { buffer->offset++; @@ -925,8 +1112,39 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) return buffer; } -/* Parse an object - create a new root, and populate. */ +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) { parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; cJSON *item = NULL; @@ -935,13 +1153,13 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return global_error.json = NULL; global_error.position = 0; - if (value == NULL) + if (value == NULL || 0 == buffer_length) { goto fail; } buffer.content = (const unsigned char*)value; - buffer.length = strlen((const char*)value) + sizeof(""); + buffer.length = buffer_length; buffer.offset = 0; buffer.hooks = global_hooks; @@ -951,7 +1169,7 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return goto fail; } - if (!parse_value(item, buffer_skip_whitespace(&buffer))) + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) { /* parse failure. ep is set. */ goto fail; @@ -998,10 +1216,8 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return { *return_parse_end = (const char*)local_error.json + local_error.position; } - else - { - global_error = local_error; - } + + global_error = local_error; } return NULL; @@ -1013,17 +1229,24 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) return cJSON_ParseWithOpts(value, 0, 0); } -#define cjson_min(a, b) ((a < b) ? a : b) +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) { + static const size_t default_buffer_size = 256; printbuffer buffer[1]; unsigned char *printed = NULL; memset(buffer, 0, sizeof(buffer)); /* create buffer */ - buffer->buffer = (unsigned char*) hooks->allocate(256); + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; buffer->format = format; buffer->hooks = *hooks; if (buffer->buffer == NULL) @@ -1041,11 +1264,11 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i /* check if reallocate is available */ if (hooks->reallocate != NULL) { - printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length); - buffer->buffer = NULL; + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); if (printed == NULL) { goto fail; } + buffer->buffer = NULL; } else /* otherwise copy the JSON over to a new buffer */ { @@ -1059,6 +1282,7 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i /* free the buffer */ hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } return printed; @@ -1067,11 +1291,13 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i if (buffer->buffer != NULL) { hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; } if (printed != NULL) { hooks->deallocate(printed); + printed = NULL; } return NULL; @@ -1111,26 +1337,28 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON if (!print_value(item, &p)) { + global_hooks.deallocate(p.buffer); + p.buffer = NULL; return NULL; } return (char*)p.buffer; } -CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) { printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; - if (len < 0) + if ((length < 0) || (buffer == NULL)) { return false; } - p.buffer = (unsigned char*)buf; - p.length = (size_t)len; + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; p.offset = 0; p.noalloc = true; - p.format = fmt; + p.format = format; p.hooks = global_hooks; return print_value(item, &p); @@ -1188,7 +1416,6 @@ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buf return parse_object(item, input_buffer); } - return false; } @@ -1239,10 +1466,6 @@ static cJSON_bool print_value(const cJSON * const item, printbuffer * const outp size_t raw_length = 0; if (item->valuestring == NULL) { - if (!output_buffer->noalloc) - { - output_buffer->hooks.deallocate(output_buffer->buffer); - } return false; } @@ -1348,6 +1571,10 @@ static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buf success: input_buffer->depth--; + if (head != NULL) { + head->prev = current_item; + } + item->type = cJSON_Array; item->child = head; @@ -1376,6 +1603,11 @@ static cJSON_bool print_array(const cJSON * const item, printbuffer * const outp return false; } + if (output_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* nesting is too deep */ + } + /* Compose the output array. */ /* opening square bracket */ output_pointer = ensure(output_buffer, 1); @@ -1483,12 +1715,17 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu current_item = new_item; } + if (cannot_access_at_index(input_buffer, 1)) + { + goto fail; /* nothing comes after the comma */ + } + /* parse the name of the child */ input_buffer->offset++; buffer_skip_whitespace(input_buffer); if (!parse_string(current_item, input_buffer)) { - goto fail; /* faile to parse name */ + goto fail; /* failed to parse name */ } buffer_skip_whitespace(input_buffer); @@ -1520,6 +1757,10 @@ static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_bu success: input_buffer->depth--; + if (head != NULL) { + head->prev = current_item; + } + item->type = cJSON_Object; item->child = head; @@ -1547,6 +1788,11 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out return false; } + if (output_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* nesting is too deep */ + } + /* Compose the output: */ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ output_pointer = ensure(output_buffer, length + 1); @@ -1608,7 +1854,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out update_offset(output_buffer); /* print comma if not last */ - length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); output_pointer = ensure(output_buffer, length + 1); if (output_pointer == NULL) { @@ -1652,17 +1898,25 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out /* Get Array size/item / object item. */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) { - cJSON *c = array->child; - size_t i = 0; - while(c) + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) { - i++; - c = c->next; + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; } /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ - return (int)i; + return (int)size; } static cJSON* get_array_item(const cJSON *array, size_t index) @@ -1706,7 +1960,7 @@ static cJSON *get_object_item(const cJSON * const object, const char * const nam current_element = object->child; if (case_sensitive) { - while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) { current_element = current_element->next; } @@ -1719,6 +1973,10 @@ static cJSON *get_object_item(const cJSON * const object, const char * const nam } } + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + return current_element; } @@ -1747,98 +2005,264 @@ static void suffix_object(cJSON *prev, cJSON *item) /* Utility for handling references. */ static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) { - cJSON *ref = cJSON_New_Item(hooks); - if (!ref) + cJSON *reference = NULL; + if (item == NULL) { return NULL; } - memcpy(ref, item, sizeof(cJSON)); - ref->string = NULL; - ref->type |= cJSON_IsReference; - ref->next = ref->prev = NULL; - return ref; + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; } -/* Add item to array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) { cJSON *child = NULL; - if ((item == NULL) || (array == NULL)) + if ((item == NULL) || (array == NULL) || (array == item)) { - return; + return false; } child = array->child; - + /* + * To find the last item in array quickly, we use prev in array + */ if (child == NULL) { /* list is empty, start new one */ array->child = item; + item->prev = item; + item->next = NULL; } else { /* append to the end */ - while (child->next) + if (child->prev) { - child = child->next; + suffix_object(child->prev, item); + array->child->prev = item; } - suffix_object(child, item); } + + return true; } -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) { - /* call cJSON_AddItemToObjectCS for code reuse */ - cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item); - /* remove cJSON_StringIsConst flag */ - item->type &= ~cJSON_StringIsConst; + return add_item_to_array(array, item); } -#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) #pragma GCC diagnostic push #endif #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wcast-qual" #endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} /* Add an item to an object with constant string as key */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) { - if (!item) + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) { - return; + return false; } - if (!(item->type & cJSON_StringIsConst) && item->string) + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) { - global_hooks.deallocate(item->string); + return false; } - item->string = (char*)string; - item->type |= cJSON_StringIsConst; - cJSON_AddItemToArray(object, item); + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; } -#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) - #pragma GCC diagnostic pop -#endif -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) { - cJSON_AddItemToArray(array, create_reference(item, &global_hooks)); + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; } -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) { - cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; } CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) { - if ((parent == NULL) || (item == NULL)) + if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL)) { return NULL; } - if (item->prev != NULL) + if (item != parent->child) { /* not the first element */ item->prev->next = item->next; @@ -1854,6 +2278,12 @@ CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const it /* first element */ parent->child = item->next; } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + /* make sure the detached item doesn't point anywhere anymore */ item->prev = NULL; item->next = NULL; @@ -1901,20 +2331,24 @@ CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const } /* Replace array/object items with new ones. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { cJSON *after_inserted = NULL; - if (which < 0) + if (which < 0 || newitem == NULL) { - return; + return false; } after_inserted = get_array_item(array, (size_t)which); if (after_inserted == NULL) { - cJSON_AddItemToArray(array, newitem); - return; + return add_item_to_array(array, newitem); + } + + if (after_inserted != array->child && after_inserted->prev == NULL) { + /* return false if after_inserted is a corrupted array item */ + return false; } newitem->next = after_inserted; @@ -1928,11 +2362,12 @@ CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newit { newitem->prev->next = newitem; } + return true; } CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) { - if ((parent == NULL) || (replacement == NULL)) + if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) { return false; } @@ -1949,14 +2384,28 @@ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON { replacement->next->prev = replacement; } - if (replacement->prev != NULL) - { - replacement->prev->next = replacement; - } if (parent->child == item) { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } parent->child = replacement; } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } item->next = NULL; item->prev = NULL; @@ -1965,19 +2414,19 @@ CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON return true; } -CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { if (which < 0) { - return; + return false; } - cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); } static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) { - if (replacement == NULL) + if ((replacement == NULL) || (string == NULL)) { return false; } @@ -1988,21 +2437,24 @@ static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSO cJSON_free(replacement->string); } replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - replacement->type &= ~cJSON_StringIsConst; + if (replacement->string == NULL) + { + return false; + } - cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + replacement->type &= ~cJSON_StringIsConst; - return true; + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); } -CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { - replace_item_in_object(object, string, newitem, false); + return replace_item_in_object(object, string, newitem, false); } -CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) { - replace_item_in_object(object, string, newitem, true); + return replace_item_in_object(object, string, newitem, true); } /* Create basic types: */ @@ -2039,12 +2491,12 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) return item; } -CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) { cJSON *item = cJSON_New_Item(&global_hooks); if(item) { - item->type = b ? cJSON_True : cJSON_False; + item->type = boolean ? cJSON_True : cJSON_False; } return item; @@ -2063,7 +2515,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) { item->valueint = INT_MAX; } - else if (num <= INT_MIN) + else if (num <= (double)INT_MIN) { item->valueint = INT_MIN; } @@ -2093,6 +2545,39 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) return item; } +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) { cJSON *item = cJSON_New_Item(&global_hooks); @@ -2140,12 +2625,13 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) cJSON *p = NULL; cJSON *a = NULL; - if (count < 0) + if ((count < 0) || (numbers == NULL)) { return NULL; } a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); @@ -2165,6 +2651,10 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) p = n; } + if (a && a->child) { + a->child->prev = n; + } + return a; } @@ -2175,7 +2665,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) cJSON *p = NULL; cJSON *a = NULL; - if (count < 0) + if ((count < 0) || (numbers == NULL)) { return NULL; } @@ -2201,6 +2691,10 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) p = n; } + if (a && a->child) { + a->child->prev = n; + } + return a; } @@ -2211,14 +2705,14 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) cJSON *p = NULL; cJSON *a = NULL; - if (count < 0) + if ((count < 0) || (numbers == NULL)) { return NULL; } a = cJSON_CreateArray(); - for(i = 0;a && (i < (size_t)count); i++) + for(i = 0; a && (i < (size_t)count); i++) { n = cJSON_CreateNumber(numbers[i]); if(!n) @@ -2237,17 +2731,21 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) p = n; } + if (a && a->child) { + a->child->prev = n; + } + return a; } -CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) { size_t i = 0; cJSON *n = NULL; cJSON *p = NULL; cJSON *a = NULL; - if (count < 0) + if ((count < 0) || (strings == NULL)) { return NULL; } @@ -2273,11 +2771,22 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) p = n; } + if (a && a->child) { + a->child->prev = n; + } + return a; } /* Duplication */ +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse); + CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + return cJSON_Duplicate_rec(item, 0, recurse ); +} + +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) { cJSON *newitem = NULL; cJSON *child = NULL; @@ -2324,7 +2833,10 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) child = item->child; while (child != NULL) { - newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if(depth >= CJSON_CIRCULAR_LIMIT) { + goto fail; + } + newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */ if (!newchild) { goto fail; @@ -2344,6 +2856,10 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) } child = child->next; } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } return newitem; @@ -2356,63 +2872,96 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) return NULL; } -CJSON_PUBLIC(void) cJSON_Minify(char *json) +static void skip_oneline_comment(char **input) { - unsigned char *into = (unsigned char*)json; - while (*json) + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) { - if (*json == ' ') - { - json++; - } - else if (*json == '\t') - { - /* Whitespace characters. */ - json++; - } - else if (*json == '\r') - { - json++; + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; } - else if (*json=='\n') + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) { - json++; + *input += static_strlen("*/"); + return; } - else if ((*json == '/') && (json[1] == '/')) - { - /* double-slash comments, to end of line. */ - while (*json && (*json != '\n')) - { - json++; - } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); } - else if ((*json == '/') && (json[1] == '*')) + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) { - /* multiline comments. */ - while (*json && !((*json == '*') && (json[1] == '/'))) - { + case ' ': + case '\t': + case '\r': + case '\n': json++; - } - json += 2; - } - else if (*json == '\"') - { - /* string literals, which are \" sensitive. */ - *into++ = (unsigned char)*json++; - while (*json && (*json != '\"')) - { - if (*json == '\\') + break; + + case '/': + if (json[1] == '/') { - *into++ = (unsigned char)*json++; + skip_oneline_comment(&json); } - *into++ = (unsigned char)*json++; - } - *into++ = (unsigned char)*json++; - } - else - { - /* All other characters. */ - *into++ = (unsigned char)*json++; + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; } } @@ -2522,7 +3071,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) { - if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) { return false; } @@ -2559,7 +3108,7 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons return true; case cJSON_Number: - if (a->valuedouble == b->valuedouble) + if (compare_double(a->valuedouble, b->valuedouble)) { return true; } @@ -2594,16 +3143,22 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons b_element = b_element->next; } + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + return true; } case cJSON_Object: { cJSON *a_element = NULL; + cJSON *b_element = NULL; cJSON_ArrayForEach(a_element, a) { /* TODO This has O(n^2) runtime, which is horrible! */ - cJSON *b_element = get_object_item(b, a_element->string, case_sensitive); + b_element = get_object_item(b, a_element->string, case_sensitive); if (b_element == NULL) { return false; @@ -2615,6 +3170,22 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons } } + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + return true; } @@ -2631,4 +3202,5 @@ CJSON_PUBLIC(void *) cJSON_malloc(size_t size) CJSON_PUBLIC(void) cJSON_free(void *object) { global_hooks.deallocate(object); + object = NULL; } diff --git a/cJSON.h b/cJSON.h index fe622eec2..cab5feb42 100644 --- a/cJSON.h +++ b/cJSON.h @@ -28,10 +28,60 @@ extern "C" { #endif +#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 3 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 + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* 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 CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#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 + /* project version */ #define CJSON_VERSION_MAJOR 1 -#define CJSON_VERSION_MINOR 5 -#define CJSON_VERSION_PATCH 3 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 19 #include @@ -74,61 +124,25 @@ typedef struct cJSON typedef struct cJSON_Hooks { - void *(*malloc_fn)(size_t sz); - void (*free_fn)(void *ptr); + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *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 +/* Limits the length of circular references can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_CIRCULAR_LIMIT +#define CJSON_CIRCULAR_LIMIT 10000 +#endif + /* returns the version of cJSON as a string */ CJSON_PUBLIC(const char*) cJSON_Version(void); @@ -138,6 +152,12 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); /* 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); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* 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 so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + /* 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. */ @@ -148,11 +168,11 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON /* 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. */ -CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); /* Returns the number of items in an array (or object). */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); @@ -161,6 +181,10 @@ CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *st /* 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. */ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + /* 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); @@ -185,24 +209,33 @@ 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. */ +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ 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); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); /* Append item to the specified array/object. */ -CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) 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` */ -CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) 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. */ -CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); -/* Remove/Detatch items from Arrays/Objects. */ +/* Remove/Detach items from Arrays/Objects. */ 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); @@ -212,42 +245,52 @@ CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); /* Update array items. */ -CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) 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); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ 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. */ + * 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. */ -CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); - +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ CJSON_PUBLIC(void) cJSON_Minify(char *json); -/* Macros for creating things quickly. */ -#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#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)) +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); /* When assigning an integer value, it needs to be propagated to valuedouble too. */ #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)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) ( \ + (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ + (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ + cJSON_Invalid\ +) /* Macro for iterating over an array or object */ #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) diff --git a/cJSON_Utils.c b/cJSON_Utils.c index 8f99c2e73..8b38eb253 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -20,18 +20,47 @@ THE SOFTWARE. */ +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUCC__ #pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + #include #include #include #include #include +#include +#include +#include + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUCC__ #pragma GCC visibility pop +#endif #include "cJSON_Utils.h" /* define our own boolean type */ +#ifdef true +#undef true +#endif #define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif #define false ((cJSON_bool)0) static unsigned char* cJSONUtils_strdup(const unsigned char* const string) @@ -79,6 +108,14 @@ static int compare_strings(const unsigned char *string1, const unsigned char *st return tolower(*string1) - tolower(*string2); } +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + + /* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */ static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive) { @@ -139,13 +176,14 @@ static void encode_string_as_pointer(unsigned char *destination, const unsigned { if (source[0] == '/') { + destination[0] = '~'; destination[1] = '1'; destination++; } else if (source[0] == '~') { destination[0] = '~'; - destination[1] = '1'; + destination[1] = '0'; destination++; } else @@ -162,6 +200,11 @@ CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const obje size_t child_index = 0; cJSON *current_child = 0; + if ((object == NULL) || (target == NULL)) + { + return NULL; + } + if (object == target) { /* found */ @@ -185,6 +228,7 @@ CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const obje if (child_index > ULONG_MAX) { cJSON_free(target_pointer); + cJSON_free(full_pointer); return NULL; } sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* / */ @@ -238,7 +282,7 @@ static cJSON_bool decode_array_index_from_pointer(const unsigned char * const po return 0; } - for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++) + for (position = 0; (pointer[position] >= '0') && (pointer[position] <= '9'); position++) { parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0'); @@ -257,6 +301,12 @@ static cJSON_bool decode_array_index_from_pointer(const unsigned char * const po static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive) { cJSON *current_element = object; + + if (pointer == NULL) + { + return NULL; + } + /* follow path of the pointer */ while ((pointer[0] == '/') && (current_element != NULL)) { @@ -279,16 +329,17 @@ static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, { current_element = current_element->next; } - /* skip to the next path token or end of string */ - while ((pointer[0] != '\0') && (pointer[0] != '/')) - { - pointer++; - } } else { return NULL; } + + /* skip to the next path token or end of string */ + while ((pointer[0] != '\0') && (pointer[0] != '/')) + { + pointer++; + } } return current_element; @@ -352,7 +403,7 @@ static cJSON *detach_item_from_array(cJSON *array, size_t which) /* item doesn't exist */ return NULL; } - if (c->prev) + if (c != array->child) { /* not the first element */ c->prev->next = c->next; @@ -361,10 +412,14 @@ static cJSON *detach_item_from_array(cJSON *array, size_t which) { c->next->prev = c->prev; } - if (c==array->child) + if (c == array->child) { array->child = c->next; } + else if (c->next == NULL) + { + array->child->prev = c->prev; + } /* make sure the detached item doesn't point anywhere anymore */ c->prev = c->next = NULL; @@ -468,6 +523,7 @@ static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) { /* Split the lists */ second->prev->next = NULL; + second->prev = NULL; } /* Recursively sort the sub-lists. */ @@ -479,7 +535,7 @@ static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) while ((first != NULL) && (second != NULL)) { cJSON *smaller = NULL; - if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, false) < 0) + if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, case_sensitive) < 0) { smaller = first; } @@ -538,6 +594,10 @@ static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) static void sort_object(cJSON * const object, const cJSON_bool case_sensitive) { + if (object == NULL) + { + return; + } object->child = sort_list(object->child, case_sensitive); } @@ -552,7 +612,7 @@ static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensiti { case cJSON_Number: /* numeric mismatch. */ - if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble)) + if ((a->valueint != b->valueint) || (!compare_double(a->valuedouble, b->valuedouble))) { return false; } @@ -846,7 +906,7 @@ static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_ if ((opcode == MOVE) || (opcode == COPY)) { cJSON *from = get_object_item(patch, "from", case_sensitive); - if (from == NULL) + if (!cJSON_IsString(from)) { /* missing "from" for copy/move. */ status = 4; @@ -900,7 +960,9 @@ static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_ /* split pointer in parent and child */ parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring); - child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); + if (parent_pointer) { + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); + } if (child_pointer != NULL) { child_pointer[0] = '\0'; @@ -942,10 +1004,23 @@ static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_ } else if (cJSON_IsObject(parent)) { - cJSON_DeleteItemFromObject(parent, (char*)child_pointer); + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer); + } + else + { + cJSON_DeleteItemFromObject(parent, (char*)child_pointer); + } cJSON_AddItemToObject(parent, (char*)child_pointer, value); value = NULL; } + else /* parent is not an object */ + { + /* Couldn't find object to add to. */ + status = 9; + goto cleanup; + } cleanup: if (value != NULL) @@ -1020,7 +1095,14 @@ CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, con static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value) { - cJSON *patch = cJSON_CreateObject(); + cJSON *patch = NULL; + + if ((patches == NULL) || (operation == NULL) || (path == NULL)) + { + return; + } + + patch = cJSON_CreateObject(); if (patch == NULL) { return; @@ -1072,7 +1154,7 @@ static void create_patches(cJSON * const patches, const unsigned char * const pa switch (from->type & 0xFF) { case cJSON_Number: - if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble)) + if ((from->valueint != to->valueint) || !compare_double(from->valuedouble, to->valuedouble)) { compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); } @@ -1198,7 +1280,14 @@ static void create_patches(cJSON * const patches, const unsigned char * const pa CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to) { - cJSON *patches = cJSON_CreateArray(); + cJSON *patches = NULL; + + if ((from == NULL) || (to == NULL)) + { + return NULL; + } + + patches = cJSON_CreateArray(); create_patches(patches, (const unsigned char*)"", from, to, false); return patches; @@ -1206,7 +1295,14 @@ CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * con CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to) { - cJSON *patches = cJSON_CreateArray(); + cJSON *patches = NULL; + + if ((from == NULL) || (to == NULL)) + { + return NULL; + } + + patches = cJSON_CreateArray(); create_patches(patches, (const unsigned char*)"", from, to, true); return patches; @@ -1271,6 +1367,7 @@ static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_ replacement = merge_patch(replace_me, patch_child, case_sensitive); if (replacement == NULL) { + cJSON_Delete(target); return NULL; } @@ -1312,6 +1409,10 @@ static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const c from_child = from->child; to_child = to->child; patch = cJSON_CreateObject(); + if (patch == NULL) + { + return NULL; + } while (from_child || to_child) { int diff; diff --git a/cJSON_Utils.h b/cJSON_Utils.h index 03ec10c9e..a970c6504 100644 --- a/cJSON_Utils.h +++ b/cJSON_Utils.h @@ -20,6 +20,14 @@ THE SOFTWARE. */ +#ifndef cJSON_Utils__h +#define cJSON_Utils__h + +#ifdef __cplusplus +extern "C" +{ +#endif + #include "cJSON.h" /* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ @@ -72,3 +80,9 @@ CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const obje /* Sorts the members of the object into alphabetical order. */ CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object); CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index fdd7126e9..1166000df 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -26,3 +26,9 @@ if (ENABLE_FUZZING) endif() + +if(ENABLE_CJSON_TEST) + ADD_EXECUTABLE(fuzz_main fuzz_main.c cjson_read_fuzzer.c) + TARGET_LINK_LIBRARIES(fuzz_main cjson) +endif() + diff --git a/fuzzing/cjson_read_fuzzer.c b/fuzzing/cjson_read_fuzzer.c new file mode 100644 index 000000000..aa9c7ba7e --- /dev/null +++ b/fuzzing/cjson_read_fuzzer.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../cJSON.h" + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); /* required by C89 */ + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + cJSON *json; + size_t offset = 4; + unsigned char *copied; + char *printed_json = NULL; + int minify, require_termination, formatted, buffered; + + + if(size <= offset) return 0; + if(data[size-1] != '\0') return 0; + if(data[0] != '1' && data[0] != '0') return 0; + if(data[1] != '1' && data[1] != '0') return 0; + if(data[2] != '1' && data[2] != '0') return 0; + if(data[3] != '1' && data[3] != '0') return 0; + + minify = data[0] == '1' ? 1 : 0; + require_termination = data[1] == '1' ? 1 : 0; + formatted = data[2] == '1' ? 1 : 0; + buffered = data[3] == '1' ? 1 : 0; + + json = cJSON_ParseWithOpts((const char*)data + offset, NULL, require_termination); + + if(json == NULL) return 0; + + if(buffered) + { + printed_json = cJSON_PrintBuffered(json, 1, formatted); + } + else + { + /* unbuffered printing */ + if(formatted) + { + printed_json = cJSON_Print(json); + } + else + { + printed_json = cJSON_PrintUnformatted(json); + } + } + + if(printed_json != NULL) free(printed_json); + + if(minify) + { + copied = (unsigned char*)malloc(size); + if(copied == NULL) return 0; + + memcpy(copied, data, size); + + cJSON_Minify((char*)copied + offset); + + free(copied); + } + + cJSON_Delete(json); + + return 0; +} + +#ifdef __cplusplus +} +#endif + diff --git a/fuzzing/fuzz_main.c b/fuzzing/fuzz_main.c new file mode 100644 index 000000000..09dc15652 --- /dev/null +++ b/fuzzing/fuzz_main.c @@ -0,0 +1,54 @@ +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); /* required by C89 */ + +/* fuzz target entry point, works without libFuzzer */ + +int main(int argc, char **argv) +{ + FILE *f; + char *buf = NULL; + long siz_buf; + + if(argc < 2) + { + fprintf(stderr, "no input file\n"); + goto err; + } + + f = fopen(argv[1], "rb"); + if(f == NULL) + { + fprintf(stderr, "error opening input file %s\n", argv[1]); + goto err; + } + + fseek(f, 0, SEEK_END); + + siz_buf = ftell(f); + rewind(f); + + if(siz_buf < 1) goto err; + + buf = (char*)malloc((size_t)siz_buf); + if(buf == NULL) + { + fprintf(stderr, "malloc() failed\n"); + goto err; + } + + if(fread(buf, (size_t)siz_buf, 1, f) != 1) + { + fprintf(stderr, "fread() failed\n"); + goto err; + } + + (void)LLVMFuzzerTestOneInput((uint8_t*)buf, (size_t)siz_buf); + +err: + free(buf); + + return 0; +} diff --git a/fuzzing/inputs/test11 b/fuzzing/inputs/test11 index 818c6e0f2..e81d60d78 100644 --- a/fuzzing/inputs/test11 +++ b/fuzzing/inputs/test11 @@ -1,8 +1,8 @@ bf{ -"name": "Jack (\"Bee\") Nimble", -"format": {"type": "rect", -"width": 1920, -"height": 1080, +"name": "Jack (\"Bee\") Nimble", +"format": {"type": "rect", +"width": 1920, +"height": 1080, "interlace": false,"frame rate": 24 } } diff --git a/fuzzing/inputs/test3 b/fuzzing/inputs/test3 index 7143163ba..11edd2f27 100644 --- a/fuzzing/inputs/test3 +++ b/fuzzing/inputs/test3 @@ -6,7 +6,7 @@ bf{"widget": { "width": 500, "height": 500 }, - "image": { + "image": { "src": "Images/Sun.png", "name": "sun1", "hOffset": 250, @@ -23,4 +23,4 @@ bf{"widget": { "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } -}} +}} diff --git a/fuzzing/inputs/test3.bu b/fuzzing/inputs/test3.bu index 6fc93d3cf..eaac4e31d 100644 --- a/fuzzing/inputs/test3.bu +++ b/fuzzing/inputs/test3.bu @@ -6,7 +6,7 @@ bu{"widget": { "width": 500, "height": 500 }, - "image": { + "image": { "src": "Images/Sun.png", "name": "sun1", "hOffset": 250, @@ -23,4 +23,4 @@ bu{"widget": { "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } -}} +}} diff --git a/fuzzing/inputs/test3.uf b/fuzzing/inputs/test3.uf index d48df6126..edad97784 100644 --- a/fuzzing/inputs/test3.uf +++ b/fuzzing/inputs/test3.uf @@ -6,7 +6,7 @@ uf{"widget": { "width": 500, "height": 500 }, - "image": { + "image": { "src": "Images/Sun.png", "name": "sun1", "hOffset": 250, @@ -23,4 +23,4 @@ uf{"widget": { "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } -}} +}} diff --git a/fuzzing/inputs/test3.uu b/fuzzing/inputs/test3.uu index ad6ae5410..1356b6d99 100644 --- a/fuzzing/inputs/test3.uu +++ b/fuzzing/inputs/test3.uu @@ -6,7 +6,7 @@ uu{"widget": { "width": 500, "height": 500 }, - "image": { + "image": { "src": "Images/Sun.png", "name": "sun1", "hOffset": 250, @@ -23,4 +23,4 @@ uu{"widget": { "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } -}} +}} diff --git a/fuzzing/inputs/test4 b/fuzzing/inputs/test4 index e24ae9b3d..943943d24 100644 --- a/fuzzing/inputs/test4 +++ b/fuzzing/inputs/test4 @@ -1,5 +1,5 @@ bf{"web-app": { - "servlet": [ + "servlet": [ { "servlet-name": "cofaxCDS", "servlet-class": "org.cofax.cds.CDSServlet", @@ -55,7 +55,7 @@ bf{"web-app": { { "servlet-name": "cofaxAdmin", "servlet-class": "org.cofax.cds.AdminServlet"}, - + { "servlet-name": "fileServlet", "servlet-class": "org.cofax.cds.FileServlet"}, @@ -82,7 +82,7 @@ bf{"web-app": { "cofaxAdmin": "/admin/*", "fileServlet": "/static/*", "cofaxTools": "/tools/*"}, - + "taglib": { "taglib-uri": "cofax.tld", "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} diff --git a/fuzzing/json.dict b/fuzzing/json.dict index ac9c08c82..9bb03d7fd 100644 --- a/fuzzing/json.dict +++ b/fuzzing/json.dict @@ -25,8 +25,8 @@ escape_sequence_r="\\r" escape_sequence_t="\\t" escape_sequence_quote="\\\"" escape_sequence_backslash="\\\\" -escapce_sequence_slash="\\/" -escpae_sequence_utf16_base="\\u" +escape_sequence_slash="\\/" +escape_sequence_utf16_base="\\u" escape_sequence_utf16="\\u12ab" number_integer="1" diff --git a/fuzzing/ossfuzz.sh b/fuzzing/ossfuzz.sh new file mode 100755 index 000000000..a2da64bf6 --- /dev/null +++ b/fuzzing/ossfuzz.sh @@ -0,0 +1,18 @@ +#!/bin/bash -eu + +# This script is meant to be run by +# https://github.com/google/oss-fuzz/blob/master/projects/cjson/Dockerfile + +mkdir build +cd build +cmake -DBUILD_SHARED_LIBS=OFF -DENABLE_CJSON_TEST=OFF .. +make -j$(nproc) + +$CXX $CXXFLAGS $SRC/cjson/fuzzing/cjson_read_fuzzer.c -I. \ + -o $OUT/cjson_read_fuzzer \ + $LIB_FUZZING_ENGINE $SRC/cjson/build/libcjson.a + +find $SRC/cjson/fuzzing/inputs -name "*" | \ + xargs zip $OUT/cjson_read_fuzzer_seed_corpus.zip + +cp $SRC/cjson/fuzzing/json.dict $OUT/cjson_read_fuzzer.dict diff --git a/library_config/cJSONConfig.cmake.in b/library_config/cJSONConfig.cmake.in index 67e83252a..909f7a9ae 100644 --- a/library_config/cJSONConfig.cmake.in +++ b/library_config/cJSONConfig.cmake.in @@ -2,8 +2,8 @@ set(CJSON_UTILS_FOUND @ENABLE_CJSON_UTILS@) # The include directories used by cJSON -set(CJSON_INCLUDE_DIRS "@prefix@/@includedir@") -set(CJSON_INCLUDE_DIR "@prefix@/@includedir@") +set(CJSON_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@") +set(CJSON_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@") get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH) diff --git a/library_config/libcjson.pc.in b/library_config/libcjson.pc.in index b3a5b405b..de48fe051 100644 --- a/library_config/libcjson.pc.in +++ b/library_config/libcjson.pc.in @@ -1,11 +1,10 @@ -prefix=@prefix@ -libdir=${prefix}/@libdir@ -includedir=${prefix}/@includedir@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: libcjson -Version: @version@ +Version: @PROJECT_VERSION@ Description: Ultralightweight JSON parser in ANSI C URL: https://github.com/DaveGamble/cJSON Libs: -L${libdir} -lcjson -Libs.Private: -lm -Cflags: -I${includedir} +Libs.private: -lm +Cflags: -I${includedir} -I${includedir}/cjson diff --git a/library_config/libcjson_utils.pc.in b/library_config/libcjson_utils.pc.in index 174ab6005..df2b4e5bc 100644 --- a/library_config/libcjson_utils.pc.in +++ b/library_config/libcjson_utils.pc.in @@ -1,11 +1,10 @@ -prefix=@prefix@ -libdir=${prefix}/@libdir@ -includedir=${prefix}/@includedir@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ Name: libcjson_utils -Version: @version@ +Version: @PROJECT_VERSION@ Description: An implementation of JSON Pointer, Patch and Merge Patch based on cJSON. URL: https://github.com/DaveGamble/cJSON Libs: -L${libdir} -lcjson_utils -Cflags: -I${includedir} +Cflags: -I${includedir} -I${includedir}/cjson Requires: libcjson diff --git a/library_config/uninstall.cmake b/library_config/uninstall.cmake new file mode 100644 index 000000000..a40066828 --- /dev/null +++ b/library_config/uninstall.cmake @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.5) + +set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") + +if(NOT EXISTS ${MANIFEST}) + message(FATAL_ERROR "Cannot find install mainfest: ${MANIFEST}") +endif() + +file(STRINGS ${MANIFEST} files) +foreach(file ${files}) + if(EXISTS ${file} OR IS_SYMLINK ${file}) + message(STATUS "Removing: ${file}") + + execute_process(COMMAND rm -f ${file} + RESULT_VARIABLE result + OUTPUT_QUIET + ERROR_VARIABLE stderr + ERROR_STRIP_TRAILING_WHITESPACE + ) + + if(NOT ${result} EQUAL 0) + message(FATAL_ERROR "${stderr}") + endif() + else() + message(STATUS "Does-not-exist: ${file}") + endif() +endforeach(file) diff --git a/test.c b/test.c index 66154e04b..986fc6eb3 100644 --- a/test.c +++ b/test.c @@ -256,7 +256,7 @@ static void create_objects(void) cJSON_Delete(root); } -int main(void) +int CJSON_CDECL main(void) { /* print the version */ printf("Version: %s\n", cJSON_Version()); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5dee1af74..c7592213b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ if(ENABLE_CJSON_TEST) - add_library(unity unity/src/unity.c) + add_library(unity STATIC unity/src/unity.c) # Disable -Werror for Unity if (FLAG_SUPPORTED_Werror) @@ -25,6 +25,14 @@ if(ENABLE_CJSON_TEST) target_compile_options(unity PRIVATE "-fno-sanitize=float-divide-by-zero") endif() endif() + # Disable -Wswitch-enum for Unity + if (FLAG_SUPPORTED_Wswitchenum) + if ("${CMAKE_VERSION}" VERSION_LESS "2.8.12") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-switch-enum") + else() + target_compile_options(unity PRIVATE "-Wno-switch-enum") + endif() + endif() #copy test files file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs") @@ -47,10 +55,11 @@ if(ENABLE_CJSON_TEST) misc_tests parse_with_opts compare_tests + cjson_add + readme_examples + minify_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) @@ -58,13 +67,16 @@ if(ENABLE_CJSON_TEST) message(WARNING "Valgrind couldn't be found.") unset(MEMORYCHECK_COMMAND) else() - set(MEMORYCHECK_COMMAND_OPTIONS --trace-children=yes --leak-check=full --error-exitcode=1) + set(MEMORYCHECK_COMMAND_OPTIONS --trace-children=yes --leak-check=full --error-exitcode=1 --suppressions=${CMAKE_CURRENT_SOURCE_DIR}/../valgrind.supp) 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("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") + target_sources(${unity_test} PRIVATE unity_setup.c) + endif() + target_link_libraries("${unity_test}" "${CJSON_LIB}" unity) if(MEMORYCHECK_COMMAND) add_test(NAME "${unity_test}" COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} "${CMAKE_CURRENT_BINARY_DIR}/${unity_test}") @@ -84,11 +96,15 @@ if(ENABLE_CJSON_TEST) set (cjson_utils_tests json_patch_tests - old_utils_tests) + old_utils_tests + misc_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) + target_link_libraries("${cjson_utils_test}" "${CJSON_LIB}" "${CJSON_UTILS_LIB}" unity) + if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") + target_sources(${cjson_utils_test} PRIVATE unity_setup.c) + endif() if(MEMORYCHECK_COMMAND) add_test(NAME "${cjson_utils_test}" COMMAND "${MEMORYCHECK_COMMAND}" ${MEMORYCHECK_COMMAND_OPTIONS} "${CMAKE_CURRENT_BINARY_DIR}/${cjson_utils_test}") diff --git a/tests/cjson_add.c b/tests/cjson_add.c new file mode 100644 index 000000000..ac96ce75d --- /dev/null +++ b/tests/cjson_add.c @@ -0,0 +1,471 @@ +/* + 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 +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static void * CJSON_CDECL failing_malloc(size_t size) +{ + (void)size; + return NULL; +} + +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void CJSON_CDECL normal_free(void *pointer) +{ + free(pointer); +} + +static cJSON_Hooks failing_hooks = { + failing_malloc, + normal_free +}; + +static void cjson_add_null_should_add_null(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *null = NULL; + + cJSON_AddNullToObject(root, "null"); + + TEST_ASSERT_NOT_NULL(null = cJSON_GetObjectItemCaseSensitive(root, "null")); + TEST_ASSERT_EQUAL_INT(null->type, cJSON_NULL); + + cJSON_Delete(root); +} + +static void cjson_add_null_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddNullToObject(NULL, "null")); + TEST_ASSERT_NULL(cJSON_AddNullToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_null_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddNullToObject(root, "null")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_true_should_add_true(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *true_item = NULL; + + cJSON_AddTrueToObject(root, "true"); + + TEST_ASSERT_NOT_NULL(true_item = cJSON_GetObjectItemCaseSensitive(root, "true")); + TEST_ASSERT_EQUAL_INT(true_item->type, cJSON_True); + + cJSON_Delete(root); +} + +static void cjson_add_true_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddTrueToObject(NULL, "true")); + TEST_ASSERT_NULL(cJSON_AddTrueToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_true_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddTrueToObject(root, "true")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_create_int_array_should_fail_on_allocation_failure(void) +{ + int numbers[] = {1, 2, 3}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateIntArray(numbers, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_create_float_array_should_fail_on_allocation_failure(void) +{ + float numbers[] = {1.0f, 2.0f, 3.0f}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateFloatArray(numbers, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_create_double_array_should_fail_on_allocation_failure(void) +{ + double numbers[] = {1.0, 2.0, 3.0}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateDoubleArray(numbers, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_create_string_array_should_fail_on_allocation_failure(void) +{ + const char* strings[] = {"1", "2", "3"}; + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_CreateStringArray(strings, 3)); + + cJSON_InitHooks(NULL); +} + +static void cjson_add_false_should_add_false(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *false_item = NULL; + + cJSON_AddFalseToObject(root, "false"); + + TEST_ASSERT_NOT_NULL(false_item = cJSON_GetObjectItemCaseSensitive(root, "false")); + TEST_ASSERT_EQUAL_INT(false_item->type, cJSON_False); + + cJSON_Delete(root); +} + +static void cjson_add_false_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddFalseToObject(NULL, "false")); + TEST_ASSERT_NULL(cJSON_AddFalseToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_false_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddFalseToObject(root, "false")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_bool_should_add_bool(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *true_item = NULL; + cJSON *false_item = NULL; + + /* true */ + cJSON_AddBoolToObject(root, "true", true); + TEST_ASSERT_NOT_NULL(true_item = cJSON_GetObjectItemCaseSensitive(root, "true")); + TEST_ASSERT_EQUAL_INT(true_item->type, cJSON_True); + + /* false */ + cJSON_AddBoolToObject(root, "false", false); + TEST_ASSERT_NOT_NULL(false_item = cJSON_GetObjectItemCaseSensitive(root, "false")); + TEST_ASSERT_EQUAL_INT(false_item->type, cJSON_False); + + cJSON_Delete(root); +} + +static void cjson_add_bool_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddBoolToObject(NULL, "false", false)); + TEST_ASSERT_NULL(cJSON_AddBoolToObject(root, NULL, false)); + + cJSON_Delete(root); +} + +static void cjson_add_bool_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddBoolToObject(root, "false", false)); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_number_should_add_number(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *number = NULL; + + cJSON_AddNumberToObject(root, "number", 42); + + TEST_ASSERT_NOT_NULL(number = cJSON_GetObjectItemCaseSensitive(root, "number")); + + TEST_ASSERT_EQUAL_INT(number->type, cJSON_Number); + TEST_ASSERT_EQUAL_DOUBLE(number->valuedouble, 42); + TEST_ASSERT_EQUAL_INT(number->valueint, 42); + + cJSON_Delete(root); +} + +static void cjson_add_number_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddNumberToObject(NULL, "number", 42)); + TEST_ASSERT_NULL(cJSON_AddNumberToObject(root, NULL, 42)); + + cJSON_Delete(root); +} + +static void cjson_add_number_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddNumberToObject(root, "number", 42)); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_string_should_add_string(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *string = NULL; + + cJSON_AddStringToObject(root, "string", "Hello World!"); + + TEST_ASSERT_NOT_NULL(string = cJSON_GetObjectItemCaseSensitive(root, "string")); + TEST_ASSERT_EQUAL_INT(string->type, cJSON_String); + TEST_ASSERT_EQUAL_STRING(string->valuestring, "Hello World!"); + + cJSON_Delete(root); +} + +static void cjson_add_string_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddStringToObject(NULL, "string", "string")); + TEST_ASSERT_NULL(cJSON_AddStringToObject(root, NULL, "string")); + + cJSON_Delete(root); +} + +static void cjson_add_string_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddStringToObject(root, "string", "string")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cjson_add_raw_should_add_raw(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *raw = NULL; + + cJSON_AddRawToObject(root, "raw", "{}"); + + TEST_ASSERT_NOT_NULL(raw = cJSON_GetObjectItemCaseSensitive(root, "raw")); + TEST_ASSERT_EQUAL_INT(raw->type, cJSON_Raw); + TEST_ASSERT_EQUAL_STRING(raw->valuestring, "{}"); + + cJSON_Delete(root); +} + +static void cjson_add_raw_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddRawToObject(NULL, "raw", "{}")); + TEST_ASSERT_NULL(cJSON_AddRawToObject(root, NULL, "{}")); + + cJSON_Delete(root); +} + +static void cjson_add_raw_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddRawToObject(root, "raw", "{}")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cJSON_add_object_should_add_object(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *object = NULL; + + cJSON_AddObjectToObject(root, "object"); + TEST_ASSERT_NOT_NULL(object = cJSON_GetObjectItemCaseSensitive(root, "object")); + TEST_ASSERT_EQUAL_INT(object->type, cJSON_Object); + + cJSON_Delete(root); +} + +static void cjson_add_object_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddObjectToObject(NULL, "object")); + TEST_ASSERT_NULL(cJSON_AddObjectToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_object_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddObjectToObject(root, "object")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +static void cJSON_add_array_should_add_array(void) +{ + cJSON *root = cJSON_CreateObject(); + cJSON *array = NULL; + + cJSON_AddArrayToObject(root, "array"); + TEST_ASSERT_NOT_NULL(array = cJSON_GetObjectItemCaseSensitive(root, "array")); + TEST_ASSERT_EQUAL_INT(array->type, cJSON_Array); + + cJSON_Delete(root); +} + +static void cjson_add_array_should_fail_with_null_pointers(void) +{ + cJSON *root = cJSON_CreateObject(); + + TEST_ASSERT_NULL(cJSON_AddArrayToObject(NULL, "array")); + TEST_ASSERT_NULL(cJSON_AddArrayToObject(root, NULL)); + + cJSON_Delete(root); +} + +static void cjson_add_array_should_fail_on_allocation_failure(void) +{ + cJSON *root = cJSON_CreateObject(); + + cJSON_InitHooks(&failing_hooks); + + TEST_ASSERT_NULL(cJSON_AddArrayToObject(root, "array")); + + cJSON_InitHooks(NULL); + + cJSON_Delete(root); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_add_null_should_add_null); + RUN_TEST(cjson_add_null_should_fail_with_null_pointers); + RUN_TEST(cjson_add_null_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_true_should_add_true); + RUN_TEST(cjson_add_true_should_fail_with_null_pointers); + RUN_TEST(cjson_add_true_should_fail_on_allocation_failure); + + RUN_TEST(cjson_create_int_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_float_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_double_array_should_fail_on_allocation_failure); + RUN_TEST(cjson_create_string_array_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_false_should_add_false); + RUN_TEST(cjson_add_false_should_fail_with_null_pointers); + RUN_TEST(cjson_add_false_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_bool_should_add_bool); + RUN_TEST(cjson_add_bool_should_fail_with_null_pointers); + RUN_TEST(cjson_add_bool_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_number_should_add_number); + RUN_TEST(cjson_add_number_should_fail_with_null_pointers); + RUN_TEST(cjson_add_number_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_string_should_add_string); + RUN_TEST(cjson_add_string_should_fail_with_null_pointers); + RUN_TEST(cjson_add_string_should_fail_on_allocation_failure); + + RUN_TEST(cjson_add_raw_should_add_raw); + RUN_TEST(cjson_add_raw_should_fail_with_null_pointers); + RUN_TEST(cjson_add_raw_should_fail_on_allocation_failure); + + RUN_TEST(cJSON_add_object_should_add_object); + RUN_TEST(cjson_add_object_should_fail_with_null_pointers); + RUN_TEST(cjson_add_object_should_fail_on_allocation_failure); + + RUN_TEST(cJSON_add_array_should_add_array); + RUN_TEST(cjson_add_array_should_fail_with_null_pointers); + RUN_TEST(cjson_add_array_should_fail_on_allocation_failure); + + return UNITY_END(); +} diff --git a/tests/common.c b/tests/common.c deleted file mode 100644 index 12022e11a..000000000 --- a/tests/common.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - 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; -} diff --git a/tests/common.h b/tests/common.h index 97c09bf23..4db6bf8c2 100644 --- a/tests/common.h +++ b/tests/common.h @@ -25,8 +25,79 @@ #include "../cJSON.c" -CJSON_PUBLIC(void) reset(cJSON *item); -CJSON_PUBLIC(char*) read_file(const char *filename); +void reset(cJSON *item); +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)); +} + +char* read_file(const char *filename); +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; +} /* 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.") diff --git a/tests/compare_tests.c b/tests/compare_tests.c index 654e14e7c..797c7740c 100644 --- a/tests/compare_tests.c +++ b/tests/compare_tests.c @@ -64,6 +64,9 @@ static void cjson_compare_should_compare_numbers(void) 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_TRUE(compare_from_string("1E100", "10E99", false)); + + TEST_ASSERT_FALSE(compare_from_string("0.5E-100", "0.5E-101", false)); TEST_ASSERT_FALSE(compare_from_string("1", "2", true)); TEST_ASSERT_FALSE(compare_from_string("1", "2", false)); @@ -148,6 +151,10 @@ static void cjson_compare_should_compare_arrays(void) 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)); + + /* Arrays that are a prefix of another array */ + TEST_ASSERT_FALSE(compare_from_string("[1,2,3]", "[1,2]", true)); + TEST_ASSERT_FALSE(compare_from_string("[1,2,3]", "[1,2]", false)); } static void cjson_compare_should_compare_objects(void) @@ -171,9 +178,18 @@ static void cjson_compare_should_compare_objects(void) "{\"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)); + /* test objects that are a subset of each other */ + TEST_ASSERT_FALSE(compare_from_string( + "{\"one\": 1, \"two\": 2}", + "{\"one\": 1, \"two\": 2, \"three\": 3}", + true)) + TEST_ASSERT_FALSE(compare_from_string( + "{\"one\": 1, \"two\": 2}", + "{\"one\": 1, \"two\": 2, \"three\": 3}", + false)) } -int main(void) +int CJSON_CDECL main(void) { UNITY_BEGIN(); diff --git a/tests/inputs/test11 b/tests/inputs/test11 index eaf43e6ef..039c61b90 100644 --- a/tests/inputs/test11 +++ b/tests/inputs/test11 @@ -1,8 +1,8 @@ { -"name": "Jack (\"Bee\") Nimble", -"format": {"type": "rect", -"width": 1920, -"height": 1080, +"name": "Jack (\"Bee\") Nimble", +"format": {"type": "rect", +"width": 1920, +"height": 1080, "interlace": false,"frame rate": 24 } } diff --git a/tests/inputs/test3 b/tests/inputs/test3 index 5662b3774..7f74acaa8 100644 --- a/tests/inputs/test3 +++ b/tests/inputs/test3 @@ -6,7 +6,7 @@ "width": 500, "height": 500 }, - "image": { + "image": { "src": "Images/Sun.png", "name": "sun1", "hOffset": 250, @@ -23,4 +23,4 @@ "alignment": "center", "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" } -}} \ No newline at end of file +}} \ No newline at end of file diff --git a/tests/inputs/test4 b/tests/inputs/test4 index d540b57f0..55cd56b17 100644 --- a/tests/inputs/test4 +++ b/tests/inputs/test4 @@ -1,5 +1,5 @@ {"web-app": { - "servlet": [ + "servlet": [ { "servlet-name": "cofaxCDS", "servlet-class": "org.cofax.cds.CDSServlet", @@ -55,7 +55,7 @@ { "servlet-name": "cofaxAdmin", "servlet-class": "org.cofax.cds.AdminServlet"}, - + { "servlet-name": "fileServlet", "servlet-class": "org.cofax.cds.FileServlet"}, @@ -82,7 +82,7 @@ "cofaxAdmin": "/admin/*", "fileServlet": "/static/*", "cofaxTools": "/tools/*"}, - + "taglib": { "taglib-uri": "cofax.tld", "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} \ No newline at end of file diff --git a/tests/json-patch-tests/cjson-utils-tests.json b/tests/json-patch-tests/cjson-utils-tests.json index 803777333..28a5e3073 100644 --- a/tests/json-patch-tests/cjson-utils-tests.json +++ b/tests/json-patch-tests/cjson-utils-tests.json @@ -80,5 +80,12 @@ "doc": { "foo": ["bar"] }, "patch": [ { "op": "add", "path": "/foo/-", "value": ["abc", "def"] }], "expected": {"foo": ["bar", ["abc", "def"]] } - } + }, + + { + "comment": "15", + "doc": {"foo": {"bar": 1}}, + "patch": [{"op": "add", "path": "/foo/bar/baz", "value": "5"}], + "error": "attempting to add to subfield of non-object" + } ] diff --git a/tests/json-patch-tests/tests.json b/tests/json-patch-tests/tests.json index 28fe4bc21..255407810 100644 --- a/tests/json-patch-tests/tests.json +++ b/tests/json-patch-tests/tests.json @@ -202,6 +202,11 @@ "patch": [{"op": "replace", "path": "", "value": {"baz": "qux"}}], "expected": {"baz": "qux"} }, + { "comment": "test replace with missing parent key should fail", + "doc": {"bar": "baz"}, + "patch": [{"op": "replace", "path": "/foo/bar", "value": false}], + "error": "replace op should fail with missing parent key" }, + { "comment": "spurious patch properties", "doc": {"foo": 1}, "patch": [{"op": "test", "path": "/foo", "value": 1, "spurious": 1}], @@ -209,6 +214,7 @@ { "doc": {"foo": null}, "patch": [{"op": "test", "path": "/foo", "value": null}], + "expected": {"foo": null}, "comment": "null value should be valid obj property" }, { "doc": {"foo": null}, @@ -238,14 +244,17 @@ { "doc": {"foo": {"foo": 1, "bar": 2}}, "patch": [{"op": "test", "path": "/foo", "value": {"bar": 2, "foo": 1}}], + "expected": {"foo": {"foo": 1, "bar": 2}}, "comment": "test should pass despite rearrangement" }, { "doc": {"foo": [{"foo": 1, "bar": 2}]}, "patch": [{"op": "test", "path": "/foo", "value": [{"bar": 2, "foo": 1}]}], + "expected": {"foo": [{"foo": 1, "bar": 2}]}, "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]}}], + "expected": {"foo": {"bar": [1, 2, 5, 4]}}, "comment": "test should pass - no error" }, { "doc": {"foo": {"bar": [1, 2, 5, 4]}}, @@ -259,7 +268,8 @@ { "comment": "Empty-string element", "doc": { "": 1 }, - "patch": [{"op": "test", "path": "/", "value": 1}] }, + "patch": [{"op": "test", "path": "/", "value": 1}], + "expected": { "": 1 } }, { "doc": { "foo": ["bar", "baz"], @@ -283,8 +293,23 @@ {"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}] }, - + {"op": "test", "path": "/m~0n", "value": 8}], + "expected": { + "": 0, + " ": 7, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "foo": [ + "bar", + "baz" + ], + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + "m~n": 8 + } + }, { "comment": "Move to same location has no effect", "doc": {"foo": 1}, "patch": [{"op": "move", "from": "/foo", "path": "/foo"}], @@ -383,11 +408,21 @@ "patch": [ { "op": "copy", "path": "/-" } ], "error": "missing 'from' parameter" }, + { "comment": "missing from location to copy", + "doc": { "foo": 1 }, + "patch": [ { "op": "copy", "from": "/bar", "path": "/foo" } ], + "error": "missing 'from' location" }, + { "comment": "missing from parameter to move", "doc": { "foo": 1 }, "patch": [ { "op": "move", "path": "" } ], "error": "missing 'from' parameter" }, + { "comment": "missing from location to move", + "doc": { "foo": 1 }, + "patch": [ { "op": "move", "from": "/bar", "path": "/foo" } ], + "error": "missing 'from' location" }, + { "comment": "duplicate ops", "doc": { "foo": "bar" }, "patch": [ { "op": "add", "path": "/baz", "value": "qux", diff --git a/tests/json_patch_tests.c b/tests/json_patch_tests.c index 3d4df4188..7c3d6aedc 100644 --- a/tests/json_patch_tests.c +++ b/tests/json_patch_tests.c @@ -66,7 +66,7 @@ static cJSON_bool test_apply_patch(const cJSON * const test) } else { - printf("Testing unkown\n"); + printf("Testing unknown\n"); } disabled = cJSON_GetObjectItemCaseSensitive(test, "disabled"); @@ -119,7 +119,7 @@ static cJSON_bool test_apply_patch(const cJSON * const test) return successful; } -static cJSON_bool test_generate_test(cJSON *test __attribute__((unused))) +static cJSON_bool test_generate_test(cJSON *test) { cJSON *doc = NULL; cJSON *patch = NULL; diff --git a/tests/minify_tests.c b/tests/minify_tests.c new file mode 100644 index 000000000..000821dbf --- /dev/null +++ b/tests/minify_tests.c @@ -0,0 +1,174 @@ +/* + Copyright (c) 2009-2019 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 +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + + +static void cjson_minify_should_not_overflow_buffer(void) +{ + char unclosed_multiline_comment[] = "/* bla"; + char pending_escape[] = "\"\\"; + + cJSON_Minify(unclosed_multiline_comment); + TEST_ASSERT_EQUAL_STRING("", unclosed_multiline_comment); + + cJSON_Minify(pending_escape); + TEST_ASSERT_EQUAL_STRING("\"\\", pending_escape); +} + +static void cjson_minify_should_remove_single_line_comments(void) +{ + const char to_minify[] = "{// this is {} \"some kind\" of [] comment /*, don't you see\n}"; + + char* minified = (char*) malloc(sizeof(to_minify)); + TEST_ASSERT_NOT_NULL(minified); + strcpy(minified, to_minify); + + cJSON_Minify(minified); + TEST_ASSERT_EQUAL_STRING("{}", minified); + + free(minified); +} + +static void cjson_minify_should_remove_spaces(void) +{ + const char to_minify[] = "{ \"key\":\ttrue\r\n }"; + + char* minified = (char*) malloc(sizeof(to_minify)); + TEST_ASSERT_NOT_NULL(minified); + strcpy(minified, to_minify); + + cJSON_Minify(minified); + TEST_ASSERT_EQUAL_STRING("{\"key\":true}", minified); + + free(minified); +} + +static void cjson_minify_should_remove_multiline_comments(void) +{ + const char to_minify[] = "{/* this is\n a /* multi\n //line \n {comment \"\\\" */}"; + + char* minified = (char*) malloc(sizeof(to_minify)); + TEST_ASSERT_NOT_NULL(minified); + strcpy(minified, to_minify); + + cJSON_Minify(minified); + TEST_ASSERT_EQUAL_STRING("{}", minified); + + free(minified); +} + +static void cjson_minify_should_not_modify_strings(void) +{ + const char to_minify[] = "\"this is a string \\\" \\t bla\""; + + char* minified = (char*) malloc(sizeof(to_minify)); + TEST_ASSERT_NOT_NULL(minified); + strcpy(minified, to_minify); + + cJSON_Minify(minified); + TEST_ASSERT_EQUAL_STRING(to_minify, minified); + + free(minified); +} + +static void cjson_minify_should_minify_json(void) { + const char to_minify[] = + "{\n" + " \"glossary\": { // comment\n" + " \"title\": \"example glossary\",\n" + " /* multi\n" + " line */\n" + " \"GlossDiv\": {\n" + " \"title\": \"S\",\n" + " \"GlossList\": {\n" + " \"GlossEntry\": {\n" + " \"ID\": \"SGML\",\n" + " \"SortAs\": \"SGML\",\n" + " \"Acronym\": \"SGML\",\n" + " \"Abbrev\": \"ISO 8879:1986\",\n" + " \"GlossDef\": {\n" + " \"GlossSeeAlso\": [\"GML\", \"XML\"]\n" + " },\n" + " \"GlossSee\": \"markup\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + const char* minified = + "{" + "\"glossary\":{" + "\"title\":\"example glossary\"," + "\"GlossDiv\":{" + "\"title\":\"S\"," + "\"GlossList\":{" + "\"GlossEntry\":{" + "\"ID\":\"SGML\"," + "\"SortAs\":\"SGML\"," + "\"Acronym\":\"SGML\"," + "\"Abbrev\":\"ISO 8879:1986\"," + "\"GlossDef\":{" + "\"GlossSeeAlso\":[\"GML\",\"XML\"]" + "}," + "\"GlossSee\":\"markup\"" + "}" + "}" + "}" + "}" + "}"; + + char *buffer = (char*) malloc(sizeof(to_minify)); + strcpy(buffer, to_minify); + + cJSON_Minify(buffer); + TEST_ASSERT_EQUAL_STRING(minified, buffer); + + free(buffer); +} + +static void cjson_minify_should_not_loop_infinitely(void) { + char string[] = { '8', ' ', '/', ' ', '5', '\n', '\0' }; + /* this should not be an infinite loop */ + cJSON_Minify(string); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_minify_should_not_overflow_buffer); + RUN_TEST(cjson_minify_should_minify_json); + RUN_TEST(cjson_minify_should_remove_single_line_comments); + RUN_TEST(cjson_minify_should_remove_multiline_comments); + RUN_TEST(cjson_minify_should_remove_spaces); + RUN_TEST(cjson_minify_should_not_modify_strings); + RUN_TEST(cjson_minify_should_not_loop_infinitely); + + return UNITY_END(); +} diff --git a/tests/misc_tests.c b/tests/misc_tests.c index 9619db2a6..fe2325e96 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -23,12 +23,12 @@ #include #include #include +#include #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]; @@ -77,7 +77,6 @@ static void cjson_get_object_item_should_get_object_items(void) 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); @@ -127,6 +126,30 @@ static void cjson_get_object_item_case_sensitive_should_get_object_items(void) cJSON_Delete(item); } +static void cjson_get_object_item_should_not_crash_with_array(void) +{ + cJSON *array = NULL; + cJSON *found = NULL; + array = cJSON_Parse("[1]"); + + found = cJSON_GetObjectItem(array, "name"); + TEST_ASSERT_NULL(found); + + cJSON_Delete(array); +} + +static void cjson_get_object_item_case_sensitive_should_not_crash_with_array(void) +{ + cJSON *array = NULL; + cJSON *found = NULL; + array = cJSON_Parse("[1]"); + + found = cJSON_GetObjectItemCaseSensitive(array, "name"); + TEST_ASSERT_NULL(found); + + cJSON_Delete(array); +} + static void typecheck_functions_should_check_type(void) { cJSON invalid[1]; @@ -197,6 +220,23 @@ static void cjson_should_not_parse_to_deeply_nested_jsons(void) TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(deep_json), "To deep JSONs should not be parsed."); } +static void cjson_should_not_follow_too_deep_circular_references(void) +{ + cJSON *o = cJSON_CreateArray(); + cJSON *a = cJSON_CreateArray(); + cJSON *b = cJSON_CreateArray(); + cJSON *x; + + cJSON_AddItemToArray(o, a); + cJSON_AddItemToArray(a, b); + cJSON_AddItemToArray(b, o); + + x = cJSON_Duplicate(o, 1); + TEST_ASSERT_NULL(x); + cJSON_DetachItemFromArray(b, 0); + cJSON_Delete(o); +} + static void cjson_set_number_value_should_set_numbers(void) { cJSON number[1] = {{NULL, NULL, NULL, cJSON_Number, NULL, 0, 0, NULL}}; @@ -233,6 +273,7 @@ static void cjson_detach_item_via_pointer_should_detach_items(void) list[3].prev = &(list[2]); list[2].prev = &(list[1]); list[1].prev = &(list[0]); + list[0].prev = &(list[3]); parent->child = &list[0]; @@ -244,7 +285,7 @@ static void cjson_detach_item_via_pointer_should_detach_items(void) /* 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."); + TEST_ASSERT_TRUE_MESSAGE((list[2].prev == &(list[3])) && (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."); @@ -257,6 +298,21 @@ static void cjson_detach_item_via_pointer_should_detach_items(void) TEST_ASSERT_NULL_MESSAGE(parent->child, "Child of the parent wasn't set to NULL."); } +static void cjson_detach_item_via_pointer_should_return_null_if_item_prev_is_null(void) +{ + cJSON list[2]; + cJSON parent[1]; + + memset(list, '\0', sizeof(list)); + + /* link the list */ + list[0].next = &(list[1]); + + parent->child = &list[0]; + TEST_ASSERT_NULL_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[1])), "Failed to detach in the middle."); + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[0])) == &(list[0]), "Failed to detach in the middle."); +} + static void cjson_replace_item_via_pointer_should_replace_items(void) { cJSON replacements[3]; @@ -279,12 +335,11 @@ static void cjson_replace_item_via_pointer_should_replace_items(void) 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].prev == end); TEST_ASSERT_TRUE(replacements[0].next == middle); TEST_ASSERT_TRUE(middle->prev == &(replacements[0])); TEST_ASSERT_TRUE(array->child == &(replacements[0])); @@ -306,16 +361,18 @@ static void cjson_replace_item_via_pointer_should_replace_items(void) static void cjson_replace_item_in_object_should_preserve_name(void) { - cJSON root[1] = {{ NULL, NULL, NULL, 0, NULL, 0, 0, NULL }}; + cJSON root[1] = {{NULL, NULL, NULL, 0, NULL, 0, 0, NULL}}; cJSON *child = NULL; cJSON *replacement = NULL; + cJSON_bool flag = false; child = cJSON_CreateNumber(1); TEST_ASSERT_NOT_NULL(child); replacement = cJSON_CreateNumber(2); TEST_ASSERT_NOT_NULL(replacement); - cJSON_AddItemToObject(root, "child", child); + flag = cJSON_AddItemToObject(root, "child", child); + TEST_ASSERT_TRUE_MESSAGE(flag, "add item to object failed"); cJSON_ReplaceItemInObject(root, "child", replacement); TEST_ASSERT_TRUE(root->child == replacement); @@ -324,7 +381,425 @@ static void cjson_replace_item_in_object_should_preserve_name(void) cJSON_Delete(replacement); } -int main(void) +static void cjson_functions_should_not_crash_with_null_pointers(void) +{ + char buffer[10]; + cJSON *item = cJSON_CreateString("item"); + cJSON *array = cJSON_CreateArray(); + cJSON *item1 = cJSON_CreateString("item1"); + cJSON *item2 = cJSON_CreateString("corrupted array item3"); + cJSON *corruptedString = cJSON_CreateString("corrupted"); + struct cJSON *originalPrev; + + add_item_to_array(array, item1); + add_item_to_array(array, item2); + + originalPrev = item2->prev; + item2->prev = NULL; + free(corruptedString->valuestring); + corruptedString->valuestring = NULL; + + cJSON_InitHooks(NULL); + TEST_ASSERT_NULL(cJSON_Parse(NULL)); + TEST_ASSERT_NULL(cJSON_ParseWithOpts(NULL, NULL, true)); + TEST_ASSERT_NULL(cJSON_Print(NULL)); + TEST_ASSERT_NULL(cJSON_PrintUnformatted(NULL)); + TEST_ASSERT_NULL(cJSON_PrintBuffered(NULL, 10, true)); + TEST_ASSERT_FALSE(cJSON_PrintPreallocated(NULL, buffer, sizeof(buffer), true)); + TEST_ASSERT_FALSE(cJSON_PrintPreallocated(item, NULL, 1, true)); + cJSON_Delete(NULL); + cJSON_GetArraySize(NULL); + TEST_ASSERT_NULL(cJSON_GetArrayItem(NULL, 0)); + TEST_ASSERT_NULL(cJSON_GetObjectItem(NULL, "item")); + TEST_ASSERT_NULL(cJSON_GetObjectItem(item, NULL)); + TEST_ASSERT_NULL(cJSON_GetObjectItemCaseSensitive(NULL, "item")); + TEST_ASSERT_NULL(cJSON_GetObjectItemCaseSensitive(item, NULL)); + TEST_ASSERT_FALSE(cJSON_HasObjectItem(NULL, "item")); + TEST_ASSERT_FALSE(cJSON_HasObjectItem(item, NULL)); + TEST_ASSERT_FALSE(cJSON_IsInvalid(NULL)); + TEST_ASSERT_FALSE(cJSON_IsFalse(NULL)); + TEST_ASSERT_FALSE(cJSON_IsTrue(NULL)); + TEST_ASSERT_FALSE(cJSON_IsBool(NULL)); + TEST_ASSERT_FALSE(cJSON_IsNull(NULL)); + TEST_ASSERT_FALSE(cJSON_IsNumber(NULL)); + TEST_ASSERT_FALSE(cJSON_IsString(NULL)); + TEST_ASSERT_FALSE(cJSON_IsArray(NULL)); + TEST_ASSERT_FALSE(cJSON_IsObject(NULL)); + TEST_ASSERT_FALSE(cJSON_IsRaw(NULL)); + TEST_ASSERT_NULL(cJSON_CreateString(NULL)); + TEST_ASSERT_NULL(cJSON_CreateRaw(NULL)); + TEST_ASSERT_NULL(cJSON_CreateIntArray(NULL, 10)); + TEST_ASSERT_NULL(cJSON_CreateFloatArray(NULL, 10)); + TEST_ASSERT_NULL(cJSON_CreateDoubleArray(NULL, 10)); + TEST_ASSERT_NULL(cJSON_CreateStringArray(NULL, 10)); + cJSON_AddItemToArray(NULL, item); + cJSON_AddItemToArray(item, NULL); + cJSON_AddItemToObject(item, "item", NULL); + cJSON_AddItemToObject(item, NULL, item); + cJSON_AddItemToObject(NULL, "item", item); + cJSON_AddItemToObjectCS(item, "item", NULL); + cJSON_AddItemToObjectCS(item, NULL, item); + cJSON_AddItemToObjectCS(NULL, "item", item); + cJSON_AddItemReferenceToArray(NULL, item); + cJSON_AddItemReferenceToArray(item, NULL); + cJSON_AddItemReferenceToObject(item, "item", NULL); + cJSON_AddItemReferenceToObject(item, NULL, item); + cJSON_AddItemReferenceToObject(NULL, "item", item); + TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(NULL, item)); + TEST_ASSERT_NULL(cJSON_DetachItemViaPointer(item, NULL)); + TEST_ASSERT_NULL(cJSON_DetachItemFromArray(NULL, 0)); + cJSON_DeleteItemFromArray(NULL, 0); + TEST_ASSERT_NULL(cJSON_DetachItemFromObject(NULL, "item")); + TEST_ASSERT_NULL(cJSON_DetachItemFromObject(item, NULL)); + TEST_ASSERT_NULL(cJSON_DetachItemFromObjectCaseSensitive(NULL, "item")); + TEST_ASSERT_NULL(cJSON_DetachItemFromObjectCaseSensitive(item, NULL)); + cJSON_DeleteItemFromObject(NULL, "item"); + cJSON_DeleteItemFromObject(item, NULL); + cJSON_DeleteItemFromObjectCaseSensitive(NULL, "item"); + cJSON_DeleteItemFromObjectCaseSensitive(item, NULL); + TEST_ASSERT_FALSE(cJSON_InsertItemInArray(array, 0, NULL)); + TEST_ASSERT_FALSE(cJSON_InsertItemInArray(array, 1, item)); + TEST_ASSERT_FALSE(cJSON_InsertItemInArray(NULL, 0, item)); + TEST_ASSERT_FALSE(cJSON_InsertItemInArray(item, 0, NULL)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(NULL, item, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(item, NULL, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemViaPointer(item, item, NULL)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInArray(item, 0, NULL)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInArray(NULL, 0, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObject(NULL, "item", item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObject(item, NULL, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObject(item, "item", NULL)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObjectCaseSensitive(NULL, "item", item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObjectCaseSensitive(item, NULL, item)); + TEST_ASSERT_FALSE(cJSON_ReplaceItemInObjectCaseSensitive(item, "item", NULL)); + TEST_ASSERT_NULL(cJSON_Duplicate(NULL, true)); + TEST_ASSERT_FALSE(cJSON_Compare(item, NULL, false)); + TEST_ASSERT_FALSE(cJSON_Compare(NULL, item, false)); + TEST_ASSERT_NULL(cJSON_SetValuestring(NULL, "test")); + TEST_ASSERT_NULL(cJSON_SetValuestring(corruptedString, "test")); + TEST_ASSERT_NULL(cJSON_SetValuestring(item, NULL)); + cJSON_Minify(NULL); + /* cJSON_SetNumberHelper should handle NULL gracefully */ + TEST_ASSERT_TRUE(isnan(cJSON_SetNumberHelper(NULL, 0))); + + /* restore corrupted item2 to delete it */ + item2->prev = originalPrev; + cJSON_Delete(corruptedString); + cJSON_Delete(array); + cJSON_Delete(item); +} + +static void cjson_set_valuestring_should_return_null_if_strings_overlap(void) +{ + cJSON *obj; + char* str; + char* str2; + + obj = cJSON_Parse("\"foo0z\""); + + str = cJSON_SetValuestring(obj, "abcde"); + str += 1; + /* The string passed to strcpy overlap which is not allowed.*/ + str2 = cJSON_SetValuestring(obj, str); + /* If it overlaps, the string will be messed up.*/ + TEST_ASSERT_TRUE(strcmp(str, "bcde") == 0); + TEST_ASSERT_NULL(str2); + cJSON_Delete(obj); +} + +static void *CJSON_CDECL failing_realloc(void *pointer, size_t size) +{ + (void)size; + (void)pointer; + return NULL; +} + +static void ensure_should_fail_on_failed_realloc(void) +{ + printbuffer buffer = {NULL, 10, 0, 0, false, false, {&malloc, &free, &failing_realloc}}; + buffer.buffer = (unsigned char *)malloc(100); + TEST_ASSERT_NOT_NULL(buffer.buffer); + + TEST_ASSERT_NULL_MESSAGE(ensure(&buffer, 200), "Ensure didn't fail with failing realloc."); +} + +static void skip_utf8_bom_should_skip_bom(void) +{ + const unsigned char string[] = "\xEF\xBB\xBF{}"; + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; + buffer.content = string; + buffer.length = sizeof(string); + buffer.hooks = global_hooks; + + TEST_ASSERT_TRUE(skip_utf8_bom(&buffer) == &buffer); + TEST_ASSERT_EQUAL_UINT(3U, (unsigned int)buffer.offset); +} + +static void skip_utf8_bom_should_not_skip_bom_if_not_at_beginning(void) +{ + const unsigned char string[] = " \xEF\xBB\xBF{}"; + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; + buffer.content = string; + buffer.length = sizeof(string); + buffer.hooks = global_hooks; + buffer.offset = 1; + + TEST_ASSERT_NULL(skip_utf8_bom(&buffer)); +} + +static void cjson_get_string_value_should_get_a_string(void) +{ + cJSON *string = cJSON_CreateString("test"); + cJSON *number = cJSON_CreateNumber(1); + + TEST_ASSERT_TRUE(cJSON_GetStringValue(string) == string->valuestring); + TEST_ASSERT_NULL(cJSON_GetStringValue(number)); + TEST_ASSERT_NULL(cJSON_GetStringValue(NULL)); + + cJSON_Delete(number); + cJSON_Delete(string); +} + +static void cjson_get_number_value_should_get_a_number(void) +{ + cJSON *string = cJSON_CreateString("test"); + cJSON *number = cJSON_CreateNumber(1); + + TEST_ASSERT_EQUAL_DOUBLE(cJSON_GetNumberValue(number), number->valuedouble); + TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(string)); + TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(NULL)); + + cJSON_Delete(number); + cJSON_Delete(string); +} + +static void cjson_create_string_reference_should_create_a_string_reference(void) +{ + const char *string = "I am a string!"; + + cJSON *string_reference = cJSON_CreateStringReference(string); + TEST_ASSERT_TRUE(string_reference->valuestring == string); + TEST_ASSERT_EQUAL_INT(cJSON_IsReference | cJSON_String, string_reference->type); + + cJSON_Delete(string_reference); +} + +static void cjson_create_object_reference_should_create_an_object_reference(void) +{ + cJSON *number_reference = NULL; + cJSON *number_object = cJSON_CreateObject(); + cJSON *number = cJSON_CreateNumber(42); + const char key[] = "number"; + + TEST_ASSERT_TRUE(cJSON_IsNumber(number)); + TEST_ASSERT_TRUE(cJSON_IsObject(number_object)); + cJSON_AddItemToObjectCS(number_object, key, number); + + number_reference = cJSON_CreateObjectReference(number); + TEST_ASSERT_TRUE(number_reference->child == number); + TEST_ASSERT_EQUAL_INT(cJSON_Object | cJSON_IsReference, number_reference->type); + + cJSON_Delete(number_object); + cJSON_Delete(number_reference); +} + +static void cjson_create_array_reference_should_create_an_array_reference(void) +{ + cJSON *number_reference = NULL; + cJSON *number_array = cJSON_CreateArray(); + cJSON *number = cJSON_CreateNumber(42); + + TEST_ASSERT_TRUE(cJSON_IsNumber(number)); + TEST_ASSERT_TRUE(cJSON_IsArray(number_array)); + cJSON_AddItemToArray(number_array, number); + + number_reference = cJSON_CreateArrayReference(number); + TEST_ASSERT_TRUE(number_reference->child == number); + TEST_ASSERT_EQUAL_INT(cJSON_Array | cJSON_IsReference, number_reference->type); + + cJSON_Delete(number_array); + cJSON_Delete(number_reference); +} + +static void cjson_add_item_to_object_or_array_should_not_add_itself(void) +{ + cJSON *object = cJSON_CreateObject(); + cJSON *array = cJSON_CreateArray(); + cJSON_bool flag = false; + + flag = cJSON_AddItemToObject(object, "key", object); + TEST_ASSERT_FALSE_MESSAGE(flag, "add an object to itself should fail"); + + flag = cJSON_AddItemToArray(array, array); + TEST_ASSERT_FALSE_MESSAGE(flag, "add an array to itself should fail"); + + cJSON_Delete(object); + cJSON_Delete(array); +} + +static void cjson_add_item_to_object_should_not_use_after_free_when_string_is_aliased(void) +{ + cJSON *object = cJSON_CreateObject(); + cJSON *number = cJSON_CreateNumber(42); + char *name = (char *)cJSON_strdup((const unsigned char *)"number", &global_hooks); + + TEST_ASSERT_NOT_NULL(object); + TEST_ASSERT_NOT_NULL(number); + TEST_ASSERT_NOT_NULL(name); + + number->string = name; + + /* The following should not have a use after free + * that would show up in valgrind or with AddressSanitizer */ + cJSON_AddItemToObject(object, number->string, number); + + cJSON_Delete(object); +} + +static void cjson_delete_item_from_array_should_not_broken_list_structure(void) +{ + const char expected_json1[] = "{\"rd\":[{\"a\":\"123\"}]}"; + const char expected_json2[] = "{\"rd\":[{\"a\":\"123\"},{\"b\":\"456\"}]}"; + const char expected_json3[] = "{\"rd\":[{\"b\":\"456\"}]}"; + char *str1 = NULL; + char *str2 = NULL; + char *str3 = NULL; + + cJSON *root = cJSON_Parse("{}"); + + cJSON *array = cJSON_AddArrayToObject(root, "rd"); + cJSON *item1 = cJSON_Parse("{\"a\":\"123\"}"); + cJSON *item2 = cJSON_Parse("{\"b\":\"456\"}"); + + cJSON_AddItemToArray(array, item1); + str1 = cJSON_PrintUnformatted(root); + TEST_ASSERT_EQUAL_STRING(expected_json1, str1); + free(str1); + + cJSON_AddItemToArray(array, item2); + str2 = cJSON_PrintUnformatted(root); + TEST_ASSERT_EQUAL_STRING(expected_json2, str2); + free(str2); + + /* this should not broken list structure */ + cJSON_DeleteItemFromArray(array, 0); + str3 = cJSON_PrintUnformatted(root); + TEST_ASSERT_EQUAL_STRING(expected_json3, str3); + free(str3); + + cJSON_Delete(root); +} + +static void cjson_set_valuestring_to_object_should_not_leak_memory(void) +{ + cJSON *root = cJSON_Parse("{}"); + const char *stringvalue = "valuestring could be changed safely"; + const char *reference_valuestring = "reference item should be freed by yourself"; + const char *short_valuestring = "shorter valuestring"; + const char *long_valuestring = "new valuestring which much longer than previous should be changed safely"; + cJSON *item1 = cJSON_CreateString(stringvalue); + cJSON *item2 = cJSON_CreateStringReference(reference_valuestring); + char *ptr1 = NULL; + char *return_value = NULL; + + cJSON_AddItemToObject(root, "one", item1); + cJSON_AddItemToObject(root, "two", item2); + + ptr1 = item1->valuestring; + return_value = cJSON_SetValuestring(cJSON_GetObjectItem(root, "one"), short_valuestring); + TEST_ASSERT_NOT_NULL(return_value); + TEST_ASSERT_EQUAL_PTR_MESSAGE(ptr1, return_value, "new valuestring shorter than old should not reallocate memory"); + TEST_ASSERT_EQUAL_STRING(short_valuestring, cJSON_GetObjectItem(root, "one")->valuestring); + + /* we needn't to free the original valuestring manually */ + ptr1 = item1->valuestring; + return_value = cJSON_SetValuestring(cJSON_GetObjectItem(root, "one"), long_valuestring); + TEST_ASSERT_NOT_NULL(return_value); + TEST_ASSERT_NOT_EQUAL_MESSAGE(ptr1, return_value, "new valuestring longer than old should reallocate memory") + TEST_ASSERT_EQUAL_STRING(long_valuestring, cJSON_GetObjectItem(root, "one")->valuestring); + + return_value = cJSON_SetValuestring(cJSON_GetObjectItem(root, "two"), long_valuestring); + TEST_ASSERT_NULL_MESSAGE(return_value, "valuestring of reference object should not be changed"); + TEST_ASSERT_EQUAL_STRING(reference_valuestring, cJSON_GetObjectItem(root, "two")->valuestring); + + cJSON_Delete(root); +} + +static void cjson_set_bool_value_must_not_break_objects(void) +{ + cJSON *bobj, *sobj, *oobj, *refobj = NULL; + + TEST_ASSERT_TRUE((cJSON_SetBoolValue(refobj, 1) == cJSON_Invalid)); + + bobj = cJSON_CreateFalse(); + TEST_ASSERT_TRUE(cJSON_IsFalse(bobj)); + TEST_ASSERT_TRUE((cJSON_SetBoolValue(bobj, 1) == cJSON_True)); + TEST_ASSERT_TRUE(cJSON_IsTrue(bobj)); + cJSON_SetBoolValue(bobj, 1); + TEST_ASSERT_TRUE(cJSON_IsTrue(bobj)); + TEST_ASSERT_TRUE((cJSON_SetBoolValue(bobj, 0) == cJSON_False)); + TEST_ASSERT_TRUE(cJSON_IsFalse(bobj)); + cJSON_SetBoolValue(bobj, 0); + TEST_ASSERT_TRUE(cJSON_IsFalse(bobj)); + + sobj = cJSON_CreateString("test"); + TEST_ASSERT_TRUE(cJSON_IsString(sobj)); + cJSON_SetBoolValue(sobj, 1); + TEST_ASSERT_TRUE(cJSON_IsString(sobj)); + cJSON_SetBoolValue(sobj, 0); + TEST_ASSERT_TRUE(cJSON_IsString(sobj)); + + oobj = cJSON_CreateObject(); + TEST_ASSERT_TRUE(cJSON_IsObject(oobj)); + cJSON_SetBoolValue(oobj, 1); + TEST_ASSERT_TRUE(cJSON_IsObject(oobj)); + cJSON_SetBoolValue(oobj, 0); + TEST_ASSERT_TRUE(cJSON_IsObject(oobj)); + + refobj = cJSON_CreateStringReference("conststring"); + TEST_ASSERT_TRUE(cJSON_IsString(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_SetBoolValue(refobj, 1); + TEST_ASSERT_TRUE(cJSON_IsString(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_SetBoolValue(refobj, 0); + TEST_ASSERT_TRUE(cJSON_IsString(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_Delete(refobj); + + refobj = cJSON_CreateObjectReference(oobj); + TEST_ASSERT_TRUE(cJSON_IsObject(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_SetBoolValue(refobj, 1); + TEST_ASSERT_TRUE(cJSON_IsObject(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_SetBoolValue(refobj, 0); + TEST_ASSERT_TRUE(cJSON_IsObject(refobj)); + TEST_ASSERT_TRUE(refobj->type & cJSON_IsReference); + cJSON_Delete(refobj); + + cJSON_Delete(oobj); + cJSON_Delete(bobj); + cJSON_Delete(sobj); +} + +static void cjson_parse_big_numbers_should_not_report_error(void) +{ + cJSON *valid_big_number_json_object1 = cJSON_Parse("{\"a\": true, \"b\": [ null,9999999999999999999999999999999999999999999999912345678901234567]}"); + cJSON *valid_big_number_json_object2 = cJSON_Parse("{\"a\": true, \"b\": [ null,999999999999999999999999999999999999999999999991234567890.1234567E3]}"); + const char *invalid_big_number_json1 = "{\"a\": true, \"b\": [ null,99999999999999999999999999999999999999999999999.1234567890.1234567]}"; + const char *invalid_big_number_json2 = "{\"a\": true, \"b\": [ null,99999999999999999999999999999999999999999999999E1234567890e1234567]}"; + + TEST_ASSERT_NOT_NULL(valid_big_number_json_object1); + TEST_ASSERT_NOT_NULL(valid_big_number_json_object2); + TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(invalid_big_number_json1), "Invalid big number JSONs should not be parsed."); + TEST_ASSERT_NULL_MESSAGE(cJSON_Parse(invalid_big_number_json2), "Invalid big number JSONs should not be parsed."); + + cJSON_Delete(valid_big_number_json_object1); + cJSON_Delete(valid_big_number_json_object2); +} + +int CJSON_CDECL main(void) { UNITY_BEGIN(); @@ -332,12 +807,32 @@ int main(void) 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(cjson_get_object_item_should_not_crash_with_array); + RUN_TEST(cjson_get_object_item_case_sensitive_should_not_crash_with_array); RUN_TEST(typecheck_functions_should_check_type); RUN_TEST(cjson_should_not_parse_to_deeply_nested_jsons); + RUN_TEST(cjson_should_not_follow_too_deep_circular_references); RUN_TEST(cjson_set_number_value_should_set_numbers); RUN_TEST(cjson_detach_item_via_pointer_should_detach_items); + RUN_TEST(cjson_detach_item_via_pointer_should_return_null_if_item_prev_is_null); RUN_TEST(cjson_replace_item_via_pointer_should_replace_items); RUN_TEST(cjson_replace_item_in_object_should_preserve_name); + RUN_TEST(cjson_functions_should_not_crash_with_null_pointers); + RUN_TEST(cjson_set_valuestring_should_return_null_if_strings_overlap); + RUN_TEST(ensure_should_fail_on_failed_realloc); + RUN_TEST(skip_utf8_bom_should_skip_bom); + RUN_TEST(skip_utf8_bom_should_not_skip_bom_if_not_at_beginning); + RUN_TEST(cjson_get_string_value_should_get_a_string); + RUN_TEST(cjson_get_number_value_should_get_a_number); + RUN_TEST(cjson_create_string_reference_should_create_a_string_reference); + RUN_TEST(cjson_create_object_reference_should_create_an_object_reference); + RUN_TEST(cjson_create_array_reference_should_create_an_array_reference); + RUN_TEST(cjson_add_item_to_object_or_array_should_not_add_itself); + RUN_TEST(cjson_add_item_to_object_should_not_use_after_free_when_string_is_aliased); + RUN_TEST(cjson_delete_item_from_array_should_not_broken_list_structure); + RUN_TEST(cjson_set_valuestring_to_object_should_not_leak_memory); + RUN_TEST(cjson_set_bool_value_must_not_break_objects); + RUN_TEST(cjson_parse_big_numbers_should_not_report_error); return UNITY_END(); } diff --git a/tests/misc_utils_tests.c b/tests/misc_utils_tests.c new file mode 100644 index 000000000..7d300bc8e --- /dev/null +++ b/tests/misc_utils_tests.c @@ -0,0 +1,80 @@ +/* + 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 +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" +#include "../cJSON_Utils.h" + +static void cjson_utils_functions_shouldnt_crash_with_null_pointers(void) +{ + cJSON *item = cJSON_CreateString("item"); + TEST_ASSERT_NOT_NULL(item); + + TEST_ASSERT_NULL(cJSONUtils_GetPointer(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_GetPointer(NULL, "pointer")); + TEST_ASSERT_NULL(cJSONUtils_GetPointerCaseSensitive(NULL, "pointer")); + TEST_ASSERT_NULL(cJSONUtils_GetPointerCaseSensitive(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_GeneratePatches(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_GeneratePatches(NULL, item)); + TEST_ASSERT_NULL(cJSONUtils_GeneratePatchesCaseSensitive(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_GeneratePatchesCaseSensitive(NULL, item)); + cJSONUtils_AddPatchToArray(item, "path", "add", NULL); + cJSONUtils_AddPatchToArray(item, "path", NULL, item); + cJSONUtils_AddPatchToArray(item, NULL, "add", item); + cJSONUtils_AddPatchToArray(NULL, "path", "add", item); + cJSONUtils_ApplyPatches(item, NULL); + cJSONUtils_ApplyPatches(NULL, item); + cJSONUtils_ApplyPatchesCaseSensitive(item, NULL); + cJSONUtils_ApplyPatchesCaseSensitive(NULL, item); + TEST_ASSERT_NULL(cJSONUtils_MergePatch(item, NULL)); + item = cJSON_CreateString("item"); + TEST_ASSERT_NULL(cJSONUtils_MergePatchCaseSensitive(item, NULL)); + item = cJSON_CreateString("item"); + /* these calls are actually valid */ + /* cJSONUtils_MergePatch(NULL, item); */ + /* cJSONUtils_MergePatchCaseSensitive(NULL, item);*/ + /* cJSONUtils_GenerateMergePatch(item, NULL); */ + /* cJSONUtils_GenerateMergePatch(NULL, item); */ + /* cJSONUtils_GenerateMergePatchCaseSensitive(item, NULL); */ + /* cJSONUtils_GenerateMergePatchCaseSensitive(NULL, item); */ + + TEST_ASSERT_NULL(cJSONUtils_FindPointerFromObjectTo(item, NULL)); + TEST_ASSERT_NULL(cJSONUtils_FindPointerFromObjectTo(NULL, item)); + cJSONUtils_SortObject(NULL); + cJSONUtils_SortObjectCaseSensitive(NULL); + + cJSON_Delete(item); +} + +int main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_utils_functions_shouldnt_crash_with_null_pointers); + + return UNITY_END(); +} diff --git a/tests/old_utils_tests.c b/tests/old_utils_tests.c index 08da6a6dc..690dbb583 100644 --- a/tests/old_utils_tests.c +++ b/tests/old_utils_tests.c @@ -90,6 +90,10 @@ static void misc_tests(void) /* Misc tests */ int numbers[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; cJSON *object = NULL; + cJSON *object1 = NULL; + cJSON *object2 = NULL; + cJSON *object3 = NULL; + cJSON *object4 = NULL; cJSON *nums = NULL; cJSON *num6 = NULL; char *pointer = NULL; @@ -112,7 +116,23 @@ static void misc_tests(void) TEST_ASSERT_EQUAL_STRING("", pointer); free(pointer); + object1 = cJSON_CreateObject(); + object2 = cJSON_CreateString("m~n"); + cJSON_AddItemToObject(object1, "m~n", object2); + pointer = cJSONUtils_FindPointerFromObjectTo(object1, object2); + TEST_ASSERT_EQUAL_STRING("/m~0n",pointer); + free(pointer); + + object3 = cJSON_CreateObject(); + object4 = cJSON_CreateString("m/n"); + cJSON_AddItemToObject(object3, "m/n", object4); + pointer = cJSONUtils_FindPointerFromObjectTo(object3, object4); + TEST_ASSERT_EQUAL_STRING("/m~1n",pointer); + free(pointer); + cJSON_Delete(object); + cJSON_Delete(object1); + cJSON_Delete(object3); } static void sort_tests(void) diff --git a/tests/parse_array.c b/tests/parse_array.c index 69e0f4114..d013f2247 100644 --- a/tests/parse_array.c +++ b/tests/parse_array.c @@ -90,7 +90,8 @@ static void parse_array_should_parse_arrays_with_one_element(void) assert_parse_array("[[]]"); assert_has_child(item); - assert_is_array(item->child); + TEST_ASSERT_NOT_NULL(item->child); + assert_has_type(item->child, cJSON_Array); assert_has_no_child(item->child); reset(item); @@ -152,7 +153,7 @@ static void parse_array_should_not_parse_non_arrays(void) assert_not_array("\"[]hello world!\n\""); } -int main(void) +int CJSON_CDECL main(void) { /* initialize cJSON item */ memset(item, 0, sizeof(cJSON)); diff --git a/tests/parse_examples.c b/tests/parse_examples.c index 7562c4572..d35d6cfb1 100644 --- a/tests/parse_examples.c +++ b/tests/parse_examples.c @@ -142,7 +142,7 @@ static void file_test6_should_not_be_parsed(void) 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."); + TEST_ASSERT_EQUAL_PTR_MESSAGE(test6, cJSON_GetErrorPtr(), "Error pointer is incorrect."); if (test6 != NULL) { @@ -179,7 +179,105 @@ static void file_test11_should_be_parsed_and_printed(void) do_test("test11"); } -int main(void) +static void test12_should_not_be_parsed(void) +{ + const char *test12 = "{ \"name\": "; + cJSON *tree = NULL; + + tree = cJSON_Parse(test12); + TEST_ASSERT_NULL_MESSAGE(tree, "Should fail to parse incomplete JSON."); + + TEST_ASSERT_EQUAL_PTR_MESSAGE(test12 + strlen(test12), cJSON_GetErrorPtr(), "Error pointer is incorrect."); + + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + +static void test13_should_be_parsed_without_null_termination(void) +{ + cJSON *tree = NULL; + const char test_13[] = "{" \ + "\"Image\":{" \ + "\"Width\":800," \ + "\"Height\":600," \ + "\"Title\":\"Viewfrom15thFloor\"," \ + "\"Thumbnail\":{" \ + "\"Url\":\"http:/*www.example.com/image/481989943\"," \ + "\"Height\":125," \ + "\"Width\":\"100\"" \ + "}," \ + "\"IDs\":[116,943,234,38793]" \ + "}" \ + "}"; + + char test_13_wo_null[sizeof(test_13) - 1]; + memcpy(test_13_wo_null, test_13, sizeof(test_13) - 1); + + tree = cJSON_ParseWithLength(test_13_wo_null, sizeof(test_13) - 1); + TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to parse valid json."); + + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + +static void test14_should_not_be_parsed(void) +{ + cJSON *tree = NULL; + const char test_14[] = "{" \ + "\"Image\":{" \ + "\"Width\":800," \ + "\"Height\":600," \ + "\"Title\":\"Viewfrom15thFloor\"," \ + "\"Thumbnail\":{" \ + "\"Url\":\"http:/*www.example.com/image/481989943\"," \ + "\"Height\":125," \ + "\"Width\":\"100\"" \ + "}," \ + "\"IDs\":[116,943,234,38793]" \ + "}" \ + "}"; + + tree = cJSON_ParseWithLength(test_14, sizeof(test_14) - 2); + TEST_ASSERT_NULL_MESSAGE(tree, "Should not continue after buffer_length is reached."); + + if (tree != NULL) + { + cJSON_Delete(tree); + } +} + +/* Address Sanitizer */ +static void test15_should_not_heap_buffer_overflow(void) +{ + const char *strings[] = { + "{\"1\":1,", + "{\"1\":1, ", + }; + + size_t i; + + for (i = 0; i < sizeof(strings) / sizeof(strings[0]); i+=1) + { + const char *json_string = strings[i]; + size_t len = strlen(json_string); + cJSON *json = NULL; + + char *exact_size_heap = (char*)malloc(len); + TEST_ASSERT_NOT_NULL(exact_size_heap); + + memcpy(exact_size_heap, json_string, len); + json = cJSON_ParseWithLength(exact_size_heap, len); + + cJSON_Delete(json); + free(exact_size_heap); + } +} + +int CJSON_CDECL main(void) { UNITY_BEGIN(); RUN_TEST(file_test1_should_be_parsed_and_printed); @@ -193,5 +291,9 @@ int main(void) 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); + RUN_TEST(test12_should_not_be_parsed); + RUN_TEST(test13_should_be_parsed_without_null_termination); + RUN_TEST(test14_should_not_be_parsed); + RUN_TEST(test15_should_not_heap_buffer_overflow); return UNITY_END(); } diff --git a/tests/parse_hex4.c b/tests/parse_hex4.c index 59b07eecc..83cbe658e 100644 --- a/tests/parse_hex4.c +++ b/tests/parse_hex4.c @@ -31,8 +31,8 @@ static void parse_hex4_should_parse_all_combinations(void) { unsigned int number = 0; - unsigned char digits_lower[5]; - unsigned char digits_upper[5]; + unsigned char digits_lower[6]; + unsigned char digits_upper[6]; /* test all combinations */ for (number = 0; number <= 0xFFFF; number++) { @@ -64,7 +64,7 @@ static void parse_hex4_should_parse_mixed_case(void) TEST_ASSERT_EQUAL_INT(0xBEEF, parse_hex4((const unsigned char*)"BEEF")); } -int main(void) +int CJSON_CDECL main(void) { UNITY_BEGIN(); RUN_TEST(parse_hex4_should_parse_all_combinations); diff --git a/tests/parse_number.c b/tests/parse_number.c index f499ab651..defda4ad5 100644 --- a/tests/parse_number.c +++ b/tests/parse_number.c @@ -48,6 +48,7 @@ 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(""); + buffer.hooks = global_hooks; TEST_ASSERT_TRUE(parse_number(item, &buffer)); assert_is_number(item); @@ -55,6 +56,17 @@ static void assert_parse_number(const char *string, int integer, double real) TEST_ASSERT_EQUAL_DOUBLE(real, item->valuedouble); } +static void assert_parse_big_number(const char *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_TRUE(parse_number(item, &buffer)); + assert_is_number(item); +} + static void parse_number_should_parse_zero(void) { assert_parse_number("0", 0, 0); @@ -96,7 +108,14 @@ static void parse_number_should_parse_negative_reals(void) assert_parse_number("-123e-128", 0, -123e-128); } -int main(void) +static void parse_number_should_parse_big_numbers(void) +{ + assert_parse_big_number("9999999999999999999999999999999999999999999999912345678901234567"); + assert_parse_big_number("9999999999999999999999999999999999999999999999912345678901234567E10"); + assert_parse_big_number("999999999999999999999999999999999999999999999991234567890.1234567"); +} + +int CJSON_CDECL main(void) { /* initialize cJSON item */ memset(item, 0, sizeof(cJSON)); @@ -106,5 +125,6 @@ int main(void) RUN_TEST(parse_number_should_parse_positive_integers); RUN_TEST(parse_number_should_parse_positive_reals); RUN_TEST(parse_number_should_parse_negative_reals); + RUN_TEST(parse_number_should_parse_big_numbers); return UNITY_END(); } diff --git a/tests/parse_object.c b/tests/parse_object.c index 09858455a..5f8e7cf1d 100644 --- a/tests/parse_object.c +++ b/tests/parse_object.c @@ -162,7 +162,7 @@ static void parse_object_should_not_parse_non_objects(void) assert_not_object("\"{}hello world!\n\""); } -int main(void) +int CJSON_CDECL main(void) { /* initialize cJSON item */ memset(item, 0, sizeof(cJSON)); diff --git a/tests/parse_string.c b/tests/parse_string.c index ceb1a8960..ce1c138ed 100644 --- a/tests/parse_string.c +++ b/tests/parse_string.c @@ -119,7 +119,7 @@ static void parse_string_should_parse_bug_94(void) reset(item); } -int main(void) +int CJSON_CDECL main(void) { /* initialize cJSON item and error pointer */ memset(item, 0, sizeof(cJSON)); diff --git a/tests/parse_value.c b/tests/parse_value.c index 08ec3e7a2..6b6b09556 100644 --- a/tests/parse_value.c +++ b/tests/parse_value.c @@ -96,7 +96,7 @@ static void parse_value_should_parse_object(void) reset(item); } -int main(void) +int CJSON_CDECL main(void) { /* initialize cJSON item */ memset(item, 0, sizeof(cJSON)); diff --git a/tests/parse_with_opts.c b/tests/parse_with_opts.c index 055746767..e390b92de 100644 --- a/tests/parse_with_opts.c +++ b/tests/parse_with_opts.c @@ -40,11 +40,23 @@ 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_EQUAL_PTR(empty_string, cJSON_GetErrorPtr()); + TEST_ASSERT_NULL(cJSON_ParseWithOpts(empty_string, &error_pointer, false)); - TEST_ASSERT_EQUAL_INT(0, error_pointer - empty_string); + TEST_ASSERT_EQUAL_PTR(empty_string, error_pointer); + TEST_ASSERT_EQUAL_PTR(empty_string, cJSON_GetErrorPtr()); +} + +static void parse_with_opts_should_handle_incomplete_json(void) +{ + const char json[] = "{ \"name\": "; + const char *parse_end = NULL; + + TEST_ASSERT_NULL(cJSON_ParseWithOpts(json, &parse_end, false)); + TEST_ASSERT_EQUAL_PTR(json + strlen(json), parse_end); + TEST_ASSERT_EQUAL_PTR(json + strlen(json), cJSON_GetErrorPtr()); } static void parse_with_opts_should_require_null_if_requested(void) @@ -65,18 +77,36 @@ static void parse_with_opts_should_return_parse_end(void) cJSON *item = cJSON_ParseWithOpts(json, &parse_end, false); TEST_ASSERT_NOT_NULL(item); - TEST_ASSERT_EQUAL_INT(2, parse_end - json); + TEST_ASSERT_EQUAL_PTR(json + 2, parse_end); cJSON_Delete(item); } -int main(void) +static void parse_with_opts_should_parse_utf8_bom(void) +{ + cJSON *with_bom = NULL; + cJSON *without_bom = NULL; + + with_bom = cJSON_ParseWithOpts("\xEF\xBB\xBF{}", NULL, true); + TEST_ASSERT_NOT_NULL(with_bom); + without_bom = cJSON_ParseWithOpts("{}", NULL, true); + TEST_ASSERT_NOT_NULL(with_bom); + + TEST_ASSERT_TRUE(cJSON_Compare(with_bom, without_bom, true)); + + cJSON_Delete(with_bom); + cJSON_Delete(without_bom); +} + +int CJSON_CDECL 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_handle_incomplete_json); RUN_TEST(parse_with_opts_should_require_null_if_requested); RUN_TEST(parse_with_opts_should_return_parse_end); + RUN_TEST(parse_with_opts_should_parse_utf8_bom); return UNITY_END(); } diff --git a/tests/print_array.c b/tests/print_array.c index 4bee17f96..7d40f2e22 100644 --- a/tests/print_array.c +++ b/tests/print_array.c @@ -87,7 +87,7 @@ static void print_array_should_print_arrays_with_multiple_elements(void) assert_print_array("[1, null, true, false, [], \"hello\", {\n\t}]", "[1,null,true,false,[],\"hello\",{}]"); } -int main(void) +int CJSON_CDECL main(void) { /* initialize cJSON item */ UNITY_BEGIN(); diff --git a/tests/print_number.c b/tests/print_number.c index 299cefc3a..3fbf9cb64 100644 --- a/tests/print_number.c +++ b/tests/print_number.c @@ -27,6 +27,8 @@ static void assert_print_number(const char *expected, double input) { unsigned char printed[1024]; + unsigned char new_buffer[26]; + unsigned int i = 0; cJSON item[1]; printbuffer buffer = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; buffer.buffer = printed; @@ -34,11 +36,29 @@ static void assert_print_number(const char *expected, double input) buffer.offset = 0; buffer.noalloc = true; buffer.hooks = global_hooks; + buffer.buffer = new_buffer; memset(item, 0, sizeof(item)); + memset(new_buffer, 0, sizeof(new_buffer)); cJSON_SetNumberValue(item, input); - TEST_ASSERT_TRUE_MESSAGE(print_number(item, &buffer), "Failed to print number."); + + /* In MinGW or visual studio(before 2015),the exponten is represented using three digits,like:"1e-009","1e+017" + * remove extra "0" to output "1e-09" or "1e+17",which makes testcase PASS */ + for(i = 0;i 3 && new_buffer[i] =='0') + { + if((new_buffer[i-3] =='e' && new_buffer[i-2] == '-' && new_buffer[i] =='0') ||(new_buffer[i-2] =='e' && new_buffer[i-1] =='+')) + { + while(new_buffer[i] !='\0') + { + new_buffer[i] = new_buffer[i+1]; + i++; + } + } + } + } TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buffer.buffer, "Printed number is not as expected."); } @@ -49,16 +69,16 @@ static void print_number_should_print_zero(void) static void print_number_should_print_negative_integers(void) { - assert_print_number("-1", -1); - assert_print_number("-32768", -32768); + assert_print_number("-1", -1.0); + assert_print_number("-32768", -32768.0); 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); + assert_print_number("1", 1.0); + assert_print_number("32767", 32767.0); + assert_print_number("2147483647", 2147483647.0); } static void print_number_should_print_positive_reals(void) @@ -68,6 +88,7 @@ static void print_number_should_print_positive_reals(void) assert_print_number("1000000000000", 10e11); assert_print_number("1.23e+129", 123e+127); assert_print_number("1.23e-126", 123e-128); + assert_print_number("3.1415926535897931", 3.1415926535897931); } static void print_number_should_print_negative_reals(void) @@ -88,7 +109,7 @@ static void print_number_should_print_non_number(void) /* assert_print_number("null", -INFTY); */ } -int main(void) +int CJSON_CDECL main(void) { /* initialize cJSON item */ UNITY_BEGIN(); diff --git a/tests/print_object.c b/tests/print_object.c index 70bbb43ce..507d97594 100644 --- a/tests/print_object.c +++ b/tests/print_object.c @@ -63,7 +63,7 @@ static void assert_print_object(const char * const expected, const char * const 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."); + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted object is not correct."); reset(item); } @@ -88,7 +88,7 @@ static void print_object_should_print_objects_with_multiple_elements(void) 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) +int CJSON_CDECL main(void) { /* initialize cJSON item */ UNITY_BEGIN(); diff --git a/tests/print_string.c b/tests/print_string.c index 83de1e7a5..e6f5b92dc 100644 --- a/tests/print_string.c +++ b/tests/print_string.c @@ -65,7 +65,7 @@ static void print_string_should_print_utf8(void) assert_print_string("\"ü猫慕\"", "ü猫慕"); } -int main(void) +int CJSON_CDECL main(void) { /* initialize cJSON item */ UNITY_BEGIN(); diff --git a/tests/print_value.c b/tests/print_value.c index d5908232c..b54db5b14 100644 --- a/tests/print_value.c +++ b/tests/print_value.c @@ -90,7 +90,7 @@ static void print_value_should_print_object(void) assert_print_value("{}"); } -int main(void) +int CJSON_CDECL main(void) { /* initialize cJSON item */ UNITY_BEGIN(); diff --git a/tests/readme_examples.c b/tests/readme_examples.c new file mode 100644 index 000000000..09850cd40 --- /dev/null +++ b/tests/readme_examples.c @@ -0,0 +1,258 @@ +/* + 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 +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" + +static const char *json = "{\n\ +\t\"name\":\t\"Awesome 4K\",\n\ +\t\"resolutions\":\t[{\n\ +\t\t\t\"width\":\t1280,\n\ +\t\t\t\"height\":\t720\n\ +\t\t}, {\n\ +\t\t\t\"width\":\t1920,\n\ +\t\t\t\"height\":\t1080\n\ +\t\t}, {\n\ +\t\t\t\"width\":\t3840,\n\ +\t\t\t\"height\":\t2160\n\ +\t\t}]\n\ +}"; + +static char* create_monitor(void) +{ + const unsigned int resolution_numbers[3][2] = { + {1280, 720}, + {1920, 1080}, + {3840, 2160} + }; + char *string = NULL; + cJSON *name = NULL; + cJSON *resolutions = NULL; + cJSON *resolution = NULL; + cJSON *width = NULL; + cJSON *height = NULL; + size_t index = 0; + + cJSON *monitor = cJSON_CreateObject(); + if (monitor == NULL) + { + goto end; + } + + name = cJSON_CreateString("Awesome 4K"); + if (name == NULL) + { + goto end; + } + /* after creation was successful, immediately add it to the monitor, + * thereby transferring ownership of the pointer to it */ + cJSON_AddItemToObject(monitor, "name", name); + + resolutions = cJSON_CreateArray(); + if (resolutions == NULL) + { + goto end; + } + cJSON_AddItemToObject(monitor, "resolutions", resolutions); + + for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) + { + resolution = cJSON_CreateObject(); + if (resolution == NULL) + { + goto end; + } + cJSON_AddItemToArray(resolutions, resolution); + + width = cJSON_CreateNumber(resolution_numbers[index][0]); + if (width == NULL) + { + goto end; + } + cJSON_AddItemToObject(resolution, "width", width); + + height = cJSON_CreateNumber(resolution_numbers[index][1]); + if (height == NULL) + { + goto end; + } + cJSON_AddItemToObject(resolution, "height", height); + } + + string = cJSON_Print(monitor); + if (string == NULL) + { + fprintf(stderr, "Failed to print monitor.\n"); + } + +end: + cJSON_Delete(monitor); + return string; +} + +static char *create_monitor_with_helpers(void) +{ + const unsigned int resolution_numbers[3][2] = { + {1280, 720}, + {1920, 1080}, + {3840, 2160} + }; + char *string = NULL; + cJSON *resolutions = NULL; + size_t index = 0; + + cJSON *monitor = cJSON_CreateObject(); + + if (cJSON_AddStringToObject(monitor, "name", "Awesome 4K") == NULL) + { + goto end; + } + + resolutions = cJSON_AddArrayToObject(monitor, "resolutions"); + if (resolutions == NULL) + { + goto end; + } + + for (index = 0; index < (sizeof(resolution_numbers) / (2 * sizeof(int))); ++index) + { + cJSON *resolution = cJSON_CreateObject(); + + if (cJSON_AddNumberToObject(resolution, "width", resolution_numbers[index][0]) == NULL) + { + goto end; + } + + if(cJSON_AddNumberToObject(resolution, "height", resolution_numbers[index][1]) == NULL) + { + goto end; + } + + cJSON_AddItemToArray(resolutions, resolution); + } + + string = cJSON_Print(monitor); + if (string == NULL) { + fprintf(stderr, "Failed to print monitor.\n"); + } + +end: + cJSON_Delete(monitor); + return string; +} + +/* return 1 if the monitor supports full hd, 0 otherwise */ +static int supports_full_hd(const char * const monitor) +{ + const cJSON *resolution = NULL; + const cJSON *resolutions = NULL; + const cJSON *name = NULL; + int status = 0; + cJSON *monitor_json = cJSON_Parse(monitor); + if (monitor_json == NULL) + { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) + { + fprintf(stderr, "Error before: %s\n", error_ptr); + } + status = 0; + goto end; + } + + name = cJSON_GetObjectItemCaseSensitive(monitor_json, "name"); + if (cJSON_IsString(name) && (name->valuestring != NULL)) + { + printf("Checking monitor \"%s\"\n", name->valuestring); + } + + resolutions = cJSON_GetObjectItemCaseSensitive(monitor_json, "resolutions"); + cJSON_ArrayForEach(resolution, resolutions) + { + cJSON *width = cJSON_GetObjectItemCaseSensitive(resolution, "width"); + cJSON *height = cJSON_GetObjectItemCaseSensitive(resolution, "height"); + + if (!cJSON_IsNumber(width) || !cJSON_IsNumber(height)) + { + status = 0; + goto end; + } + + if (compare_double(width->valuedouble, 1920) && compare_double(height->valuedouble, 1080)) + { + status = 1; + goto end; + } + } + +end: + cJSON_Delete(monitor_json); + return status; +} + +static void create_monitor_should_create_a_monitor(void) +{ + char *monitor = create_monitor(); + + TEST_ASSERT_EQUAL_STRING(monitor, json); + + free(monitor); +} + +static void create_monitor_with_helpers_should_create_a_monitor(void) +{ + char *monitor = create_monitor_with_helpers(); + + TEST_ASSERT_EQUAL_STRING(json, monitor); + + free(monitor); +} + +static void supports_full_hd_should_check_for_full_hd_support(void) +{ + static const char *monitor_without_hd = "{\n\ +\t\t\"name\": \"lame monitor\",\n\ +\t\t\"resolutions\":\t[{\n\ +\t\t\t\"width\":\t640,\n\ +\t\t\t\"height\":\t480\n\ +\t\t}]\n\ +}"; + + TEST_ASSERT(supports_full_hd(json)); + TEST_ASSERT_FALSE(supports_full_hd(monitor_without_hd)); +} + +int CJSON_CDECL main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(create_monitor_should_create_a_monitor); + RUN_TEST(create_monitor_with_helpers_should_create_a_monitor); + RUN_TEST(supports_full_hd_should_check_for_full_hd_support); + + return UNITY_END(); +} diff --git a/tests/unity/.travis.yml b/tests/unity/.travis.yml index 8ae9fa0c5..bd165b1e7 100644 --- a/tests/unity/.travis.yml +++ b/tests/unity/.travis.yml @@ -18,7 +18,9 @@ install: script: - cd test && rake ci - make -s - - make -s DEBUG=-m32 + - make -s DEBUG=-m32 #32-bit architecture with 64-bit support + - make -s DEBUG=-m32 UNITY_SUPPORT_64= #32-bit build without 64-bit types + - make -s UNITY_INCLUDE_DOUBLE= # without double - cd ../extras/fixture/test && rake ci - make -s default noStdlibMalloc - make -s C89 diff --git a/tests/unity/README.md b/tests/unity/README.md index 17ab57474..ec73b4a1b 100644 --- a/tests/unity/README.md +++ b/tests/unity/README.md @@ -118,6 +118,17 @@ Another way of calling TEST_ASSERT_EQUAL_INT Asserts that the actual value is within plus or minus delta of the expected value. This also comes in size specific variants. + + TEST_ASSERT_GREATER_THAN(threshold, actual) + +Asserts that the actual value is greater than the threshold. This also comes in size specific variants. + + + TEST_ASSERT_LESS_THAN(threshold, actual) + +Asserts that the actual value is less than the threshold. This also comes in size specific variants. + + Arrays ------ diff --git a/tests/unity/auto/generate_module.rb b/tests/unity/auto/generate_module.rb index ade4f1a96..13b4cc74d 100644 --- a/tests/unity/auto/generate_module.rb +++ b/tests/unity/auto/generate_module.rb @@ -172,7 +172,7 @@ def create_filename(part1, part2 = '') when 'camel' then part1 when 'snake' then part1.downcase when 'caps' then part1.upcase - else part1.downcase + else part1 end else case (@options[:naming]) @@ -180,7 +180,7 @@ def create_filename(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 + else part1 + '_' + part2 end end end @@ -290,7 +290,7 @@ def destroy(module_name, pattern = nil) ' -n"camel" sets the file naming convention.', ' bumpy - BumpyCaseFilenames.', ' camel - camelCaseFilenames.', - ' snake - snake_case_filenames. (DEFAULT)', + ' snake - snake_case_filenames.', ' caps - CAPS_CASE_FILENAMES.', ' -u update subversion too (requires subversion command line)', ' -y"my.yml" selects a different yaml config file for module generation', diff --git a/tests/unity/auto/generate_test_runner.rb b/tests/unity/auto/generate_test_runner.rb index 2c5e56a9a..344a2d0fb 100644 --- a/tests/unity/auto/generate_test_runner.rb +++ b/tests/unity/auto/generate_test_runner.rb @@ -119,7 +119,7 @@ def find_tests(source) 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]}/ + next unless line =~ /\s+#{tests_and_line_numbers[i][:test]}(?:\s|\()/ source_index += index tests_and_line_numbers[i][:line_number] = source_index + 1 break @@ -157,6 +157,9 @@ 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('#ifdef __WIN32__') + output.puts('#define UNITY_INCLUDE_SETUP_STUBS') + output.puts('#endif') output.puts("#include \"#{@options[:framework]}.h\"") output.puts('#include "cmock.h"') unless mocks.empty? output.puts('#include ') @@ -235,22 +238,36 @@ def create_mock_management(output, mock_headers) 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]) + if @options[:suite_setup].nil? + # New style, call suiteSetUp() if we can use weak symbols + output.puts('#if defined(UNITY_WEAK_ATTRIBUTE) || defined(UNITY_WEAK_PRAGMA)') + output.puts(' suiteSetUp();') + output.puts('#endif') + else + # Old style, C code embedded in the :suite_setup option + output.puts(@options[:suite_setup]) + end 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]) + if @options[:suite_teardown].nil? + # New style, call suiteTearDown() if we can use weak symbols + output.puts('#if defined(UNITY_WEAK_ATTRIBUTE) || defined(UNITY_WEAK_PRAGMA)') + output.puts(' return suiteTearDown(num_failures);') + output.puts('#else') + output.puts(' return num_failures;') + output.puts('#endif') + else + # Old style, C code embedded in the :suite_teardown option + output.puts(@options[:suite_teardown]) + end output.puts('}') end @@ -342,7 +359,7 @@ def create_main(output, filename, tests, used_mocks) output.puts("int #{main_name}(void)") output.puts('{') end - output.puts(' suite_setup();') unless @options[:suite_setup].nil? + output.puts(' suite_setup();') output.puts(" UnityBegin(\"#{filename.gsub(/\\/, '\\\\\\')}\");") if @options[:use_param_tests] tests.each do |test| @@ -357,7 +374,7 @@ def create_main(output, filename, tests, used_mocks) end output.puts output.puts(' CMock_Guts_MemFreeFinal();') unless used_mocks.empty? - output.puts(" return #{@options[:suite_teardown].nil? ? '' : 'suite_teardown'}(UnityEnd());") + output.puts(" return suite_teardown(UnityEnd());") output.puts('}') end diff --git a/tests/unity/auto/parse_output.rb b/tests/unity/auto/parse_output.rb index f16cdb036..8e88fff04 100644 --- a/tests/unity/auto/parse_output.rb +++ b/tests/unity/auto/parse_output.rb @@ -78,7 +78,7 @@ def test_passed_unity_fixture(array) @array_list.push ' ' end - # Test was flagged as being ingored so format the output + # Test was flagged as being ignored so format the output def test_ignored(array) last_item = array.length - 1 test_name = array[last_item - 2] diff --git a/tests/unity/auto/stylize_as_junit.rb b/tests/unity/auto/stylize_as_junit.rb old mode 100644 new mode 100755 diff --git a/tests/unity/docs/UnityAssertionsReference.md b/tests/unity/docs/UnityAssertionsReference.md index 558f6dbb1..2dcf5e3a2 100644 --- a/tests/unity/docs/UnityAssertionsReference.md +++ b/tests/unity/docs/UnityAssertionsReference.md @@ -290,6 +290,60 @@ Asserts the specified bit of the `actual` parameter is high. Asserts the specified bit of the `actual` parameter is low. +### Integer Less Than / Greater Than + +These assertions verify that the `actual` parameter is less than or greater +than `threshold` (exclusive). For example, if the threshold value is 0 for the +greater than assertion will fail if it is 0 or less. + +##### `TEST_ASSERT_GREATER_THAN (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_INT (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_INT8 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_INT16 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_INT32 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_UINT (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_UINT8 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_UINT16 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_UINT32 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_HEX8 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_HEX16 (threshold, actual)` + +##### `TEST_ASSERT_GREATER_THAN_HEX32 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_INT (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_INT8 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_INT16 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_INT32 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_UINT (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_UINT8 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_UINT16 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_UINT32 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_HEX8 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_HEX16 (threshold, actual)` + +##### `TEST_ASSERT_LESS_THAN_HEX32 (threshold, actual)` + ### Integer Ranges (of all sizes) diff --git a/tests/unity/docs/UnityConfigurationGuide.md b/tests/unity/docs/UnityConfigurationGuide.md index 283d7799a..96e5358d4 100644 --- a/tests/unity/docs/UnityConfigurationGuide.md +++ b/tests/unity/docs/UnityConfigurationGuide.md @@ -79,18 +79,7 @@ _Example:_ #define UNITY_EXCLUDE_LIMITS_H -##### `UNITY_EXCLUDE_SIZEOF` - -The third and final attempt to guess your types is to use the `sizeof()` -operator. Even if the first two options don't work, this one covers most cases. -There _is_ a rare compiler or two out there that doesn't support sizeof() in the -preprocessing stage, though. For these, you have the ability to disable this -feature as well. - -_Example:_ - #define UNITY_EXCLUDE_SIZEOF - -If you've disabled all of the automatic options above, you're going to have to +If you've disabled both of the automatic options above, you're going to have to do the configuration yourself. Don't worry. Even this isn't too bad... there are just a handful of defines that you are going to specify if you don't like the defaults. @@ -127,7 +116,7 @@ _Example:_ #define UNITY_POINTER_WIDTH 64 -##### `UNITY_INCLUDE_64` +##### `UNITY_SUPPORT_64` Unity will automatically include 64-bit support if it auto-detects it, or if your `int`, `long`, or pointer widths are greater than 32-bits. Define this to @@ -136,7 +125,7 @@ can be a significant size and speed impact to enabling 64-bit support on small targets, so don't define it if you don't need it. _Example:_ - #define UNITY_INCLUDE_64 + #define UNITY_SUPPORT_64 ### Floating Point Types @@ -170,24 +159,20 @@ _Example:_ #define UNITY_INCLUDE_DOUBLE -##### `UNITY_FLOAT_VERBOSE` - -##### `UNITY_DOUBLE_VERBOSE` +##### `UNITY_EXCLUDE_FLOAT_PRINT` Unity aims for as small of a footprint as possible and avoids most standard -library calls (some embedded platforms don't have a standard library!). Because +library calls (some embedded platforms don’t have a standard library!). Because of this, its routines for printing integer values are minimalist and hand-coded. -To keep Unity universal, though, we chose to _not_ develop our own floating -point print routines. Instead, the display of floating point values during a -failure are optional. By default, Unity will not print the actual results of -floating point assertion failure. So a failed assertion will produce a message -like `"Values Not Within Delta"`. If you would like verbose failure messages for -floating point assertions, use these options to give more explicit failure -messages (e.g. `"Expected 4.56 Was 4.68"`). Note that this feature requires the -use of `sprintf` so might not be desirable in all cases. +Therefore, the display of floating point values during a failure are optional. +By default, Unity will print the actual results of floating point assertion +failure (e.g. ”Expected 4.56 Was 4.68”). To not include this extra support, you +can use this define to instead respond to a failed assertion with a message like +”Values Not Within Delta”. If you would like verbose failure messages for floating +point assertions, use these options to give more explicit failure messages. _Example:_ - #define UNITY_DOUBLE_VERBOSE + #define UNITY_EXCLUDE_FLOAT_PRINT ##### `UNITY_FLOAT_TYPE` @@ -277,25 +262,32 @@ will declare an instance of your function by default. If you want to disable this behavior, add `UNITY_OMIT_OUTPUT_FLUSH_HEADER_DECLARATION`. -##### `UNITY_SUPPORT_WEAK` +##### `UNITY_WEAK_ATTRIBUTE` + +##### `UNITY_WEAK_PRAGMA` + +##### `UNITY_NO_WEAK` -For some targets, Unity can make the otherwise required `setUp()` and -`tearDown()` functions optional. This is a nice convenience for test writers -since `setUp` and `tearDown` don't often actually _do_ anything. If you're using -gcc or clang, this option is automatically defined for you. Other compilers can -also support this behavior, if they support a C feature called weak functions. A -weak function is a function that is compiled into your executable _unless_ a -non-weak version of the same function is defined elsewhere. If a non-weak -version is found, the weak version is ignored as if it never existed. If your -compiler supports this feature, you can let Unity know by defining -`UNITY_SUPPORT_WEAK` as the function attributes that would need to be applied to -identify a function as weak. If your compiler lacks support for weak functions, -you will always need to define `setUp` and `tearDown` functions (though they can -be and often will be just empty). The most common options for this feature are: +For some targets, Unity can make the otherwise required setUp() and tearDown() +functions optional. This is a nice convenience for test writers since setUp and +tearDown don’t often actually do anything. If you’re using gcc or clang, this +option is automatically defined for you. Other compilers can also support this +behavior, if they support a C feature called weak functions. A weak function is +a function that is compiled into your executable unless a non-weak version of +the same function is defined elsewhere. If a non-weak version is found, the weak +version is ignored as if it never existed. If your compiler supports this feature, +you can let Unity know by defining UNITY_WEAK_ATTRIBUTE or UNITY_WEAK_PRAGMA as +the function attributes that would need to be applied to identify a function as +weak. If your compiler lacks support for weak functions, you will always need to +define setUp and tearDown functions (though they can be and often will be just +empty). You can also force Unity to NOT use weak functions by defining +UNITY_NO_WEAK. The most common options for this feature are: _Example:_ - #define UNITY_SUPPORT_WEAK weak - #define UNITY_SUPPORT_WEAK __attribute__((weak)) + #define UNITY_WEAK_ATTRIBUTE weak + #define UNITY_WEAK_ATTRIBUTE __attribute__((weak)) + #define UNITY_WEAK_PRAGMA + #define UNITY_NO_WEAK ##### `UNITY_PTR_ATTRIBUTE` @@ -309,6 +301,51 @@ _Example:_ #define UNITY_PTR_ATTRIBUTE near +##### `UNITY_PRINT_EOL` + +By default, Unity outputs \n at the end of each line of output. This is easy +to parse by the scripts, by Ceedling, etc, but it might not be ideal for YOUR +system. Feel free to override this and to make it whatever you wish. + +_Example:_ + #define UNITY_PRINT_EOL { UNITY_OUTPUT_CHAR('\r'); UNITY_OUTPUT_CHAR('\n') } + + + +##### `UNITY_EXCLUDE_DETAILS` + +This is an option for if you absolutely must squeeze every byte of memory out of +your system. Unity stores a set of internal scratchpads which are used to pass +extra detail information around. It's used by systems like CMock in order to +report which function or argument flagged an error. If you're not using CMock and +you're not using these details for other things, then you can exclude them. + +_Example:_ + #define UNITY_EXCLUDE_DETAILS + + + +##### `UNITY_EXCLUDE_SETJMP` + +If your embedded system doesn't support the standard library setjmp, you can +exclude Unity's reliance on this by using this define. This dropped dependence +comes at a price, though. You will be unable to use custom helper functions for +your tests, and you will be unable to use tools like CMock. Very likely, if your +compiler doesn't support setjmp, you wouldn't have had the memory space for those +things anyway, though... so this option exists for those situations. + +_Example:_ + #define UNITY_EXCLUDE_SETJMP + +##### `UNITY_OUTPUT_COLOR` + +If you want to add color using ANSI escape codes you can use this define. +t +_Example:_ + #define UNITY_OUTPUT_COLOR + + + ## Getting Into The Guts There will be cases where the options above aren't quite going to get everything diff --git a/tests/unity/docs/UnityGettingStartedGuide.md b/tests/unity/docs/UnityGettingStartedGuide.md index 50fc91c70..08888a12b 100644 --- a/tests/unity/docs/UnityGettingStartedGuide.md +++ b/tests/unity/docs/UnityGettingStartedGuide.md @@ -72,7 +72,7 @@ header files. These three files _are_ Unity. into this folder already. This is where all the handy documentation can be found. - `examples` - This contains a few examples of using Unity. -- `extras` - These are optional add ons to Unity that are not part of the core +- `extras` - These are optional addons to Unity that are not part of the core project. If you've reached us through James Grenning's book, you're going to want to look here. - `test` - This is how Unity and its scripts are all tested. If you're just using diff --git a/tests/unity/docs/UnityHelperScriptsGuide.md b/tests/unity/docs/UnityHelperScriptsGuide.md index 3a9300964..424299008 100644 --- a/tests/unity/docs/UnityHelperScriptsGuide.md +++ b/tests/unity/docs/UnityHelperScriptsGuide.md @@ -124,7 +124,7 @@ demonstrates using a Ruby hash. ##### `:includes` -This option specifies an array of file names to be ?#include?'d at the top of +This option specifies an array of file names to be `#include`'d at the top of your runner C file. You might use it to reference custom types or anything else universally needed in your generated runners. @@ -133,11 +133,23 @@ universally needed in your generated runners. Define this option with C code to be executed _before any_ test cases are run. +Alternatively, if your C compiler supports weak symbols, you can leave this +option unset and instead provide a `void suiteSetUp(void)` function in your test +suite. The linker will look for this symbol and fall back to a Unity-provided +stub if it is not found. + ##### `:suite_teardown` -Define this option with C code to be executed ?after all?test cases have -finished. +Define this option with C code to be executed _after all_ test cases have +finished. An integer variable `num_failures` is available for diagnostics. +The code should end with a `return` statement; the value returned will become +the exit code of `main`. You can normally just return `num_failures`. + +Alternatively, if your C compiler supports weak symbols, you can leave this +option unset and instead provide a `int suiteTearDown(int num_failures)` +function in your test suite. The linker will look for this symbol and fall +back to a Unity-provided stub if it is not found. ##### `:enforce_strict_ordering` diff --git a/tests/unity/examples/unity_config.h b/tests/unity/examples/unity_config.h index eb53517bf..a2f161a7a 100644 --- a/tests/unity/examples/unity_config.h +++ b/tests/unity/examples/unity_config.h @@ -201,10 +201,12 @@ * `stdout` option. You decide to route your test result output to a custom * serial `RS232_putc()` function you wrote like thus: */ -/* #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) */ -/* #define UNITY_OUTPUT_FLUSH() RS232_flush() */ -/* #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) */ -/* #define UNITY_OUTPUT_COMPLETE() RS232_close() */ +/* #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) */ +/* #define UNITY_OUTPUT_CHAR_HEADER_DECLARATION RS232_putc(int) */ +/* #define UNITY_OUTPUT_FLUSH() RS232_flush() */ +/* #define UNITY_OUTPUT_FLUSH_HEADER_DECLARATION RS232_flush(void) */ +/* #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) */ +/* #define UNITY_OUTPUT_COMPLETE() RS232_close() */ /* For some targets, Unity can make the otherwise required `setUp()` and * `tearDown()` functions optional. This is a nice convenience for test writers diff --git a/tests/unity/extras/eclipse/error_parsers.txt b/tests/unity/extras/eclipse/error_parsers.txt index 94e34ff38..f7ce8bd02 100644 --- a/tests/unity/extras/eclipse/error_parsers.txt +++ b/tests/unity/extras/eclipse/error_parsers.txt @@ -2,7 +2,7 @@ Eclipse error parsers ===================== These are a godsend for extracting & quickly navigating to -warnings & error messages from console output. Unforunately +warnings & error messages from console output. Unfortunately I don't know how to write an Eclipse plugin so you'll have to add them manually. diff --git a/tests/unity/extras/fixture/rakefile_helper.rb b/tests/unity/extras/fixture/rakefile_helper.rb index 5aa8e56af..c45b2393d 100644 --- a/tests/unity/extras/fixture/rakefile_helper.rb +++ b/tests/unity/extras/fixture/rakefile_helper.rb @@ -53,7 +53,7 @@ def build_compiler_fields defines = if $cfg['compiler']['defines']['items'].nil? '' else - squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items'] + ['UNITY_OUTPUT_CHAR=UnityOutputCharSpy_OutputChar']) + squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items'] + ['UNITY_OUTPUT_CHAR=UnityOutputCharSpy_OutputChar'] + ['UNITY_OUTPUT_CHAR_HEADER_DECLARATION=UnityOutputCharSpy_OutputChar\(int\)']) end options = squash('', $cfg['compiler']['options']) includes = squash($cfg['compiler']['includes']['prefix'], $cfg['compiler']['includes']['items']) diff --git a/tests/unity/extras/fixture/test/Makefile b/tests/unity/extras/fixture/test/Makefile index 80e124f43..e6c62552f 100644 --- a/tests/unity/extras/fixture/test/Makefile +++ b/tests/unity/extras/fixture/test/Makefile @@ -6,6 +6,7 @@ endif CFLAGS += -std=c99 -pedantic -Wall -Wextra -Werror CFLAGS += $(DEBUG) DEFINES = -D UNITY_OUTPUT_CHAR=UnityOutputCharSpy_OutputChar +DEFINES += -D UNITY_OUTPUT_CHAR_HEADER_DECLARATION=UnityOutputCharSpy_OutputChar\(int\) SRC = ../src/unity_fixture.c \ ../../../src/unity.c \ unity_fixture_Test.c \ diff --git a/tests/unity/release/build.info b/tests/unity/release/build.info index 207634b2e..56d591286 100644 --- a/tests/unity/release/build.info +++ b/tests/unity/release/build.info @@ -1,2 +1,2 @@ -120 +122 diff --git a/tests/unity/release/version.info b/tests/unity/release/version.info index 3b904b68c..cf12b30d2 100644 --- a/tests/unity/release/version.info +++ b/tests/unity/release/version.info @@ -1,2 +1,2 @@ -2.4.1 +2.4.3 diff --git a/tests/unity/src/unity.c b/tests/unity/src/unity.c index 177af0f43..eee6f965e 100644 --- a/tests/unity/src/unity.c +++ b/tests/unity/src/unity.c @@ -4,10 +4,11 @@ [Released under MIT License. Please refer to license.txt for details] ============================================================================ */ +#define UNITY_INCLUDE_SETUP_STUBS #include "unity.h" #include -/* If omitted from header, declare overrideable prototypes here so they're ready for use */ +/* If omitted from header, declare overridable prototypes here so they're ready for use */ #ifdef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION void UNITY_OUTPUT_CHAR(int); #endif @@ -19,14 +20,24 @@ void UNITY_OUTPUT_CHAR(int); struct UNITY_STORAGE_T Unity; +#ifdef UNITY_OUTPUT_COLOR +static const char UnityStrOk[] = "\033[42mOK\033[00m"; +static const char UnityStrPass[] = "\033[42mPASS\033[00m"; +static const char UnityStrFail[] = "\033[41mFAIL\033[00m"; +static const char UnityStrIgnore[] = "\033[43mIGNORE\033[00m"; +#else static const char UnityStrOk[] = "OK"; static const char UnityStrPass[] = "PASS"; static const char UnityStrFail[] = "FAIL"; static const char UnityStrIgnore[] = "IGNORE"; +#endif static const char UnityStrNull[] = "NULL"; static const char UnityStrSpacer[] = ". "; static const char UnityStrExpected[] = " Expected "; static const char UnityStrWas[] = " Was "; +static const char UnityStrGt[] = " to be greater than "; +static const char UnityStrLt[] = " to be less than "; +static const char UnityStrOrEqual[] = "or equal to "; static const char UnityStrElement[] = " Element "; static const char UnityStrByte[] = " Byte "; static const char UnityStrMemory[] = " Memory Mismatch."; @@ -81,6 +92,18 @@ void UnityPrint(const char* string) UNITY_OUTPUT_CHAR('\\'); UNITY_OUTPUT_CHAR('n'); } +#ifdef UNITY_OUTPUT_COLOR + /* print ANSI escape code */ + else if (*pch == 27 && *(pch + 1) == '[') + { + while (*pch && *pch != 'm') + { + UNITY_OUTPUT_CHAR(*pch); + pch++; + } + UNITY_OUTPUT_CHAR('m'); + } +#endif /* unprintable characters are shown as codes */ else { @@ -235,95 +258,97 @@ void UnityPrintMask(const UNITY_UINT mask, const UNITY_UINT number) /*-----------------------------------------------*/ #ifndef UNITY_EXCLUDE_FLOAT_PRINT -static void UnityPrintDecimalAndNumberWithLeadingZeros(UNITY_INT32 fraction_part, UNITY_INT32 divisor) -{ - UNITY_OUTPUT_CHAR('.'); - while (divisor > 0) - { - UNITY_OUTPUT_CHAR('0' + fraction_part / divisor); - fraction_part %= divisor; - divisor /= 10; - if (fraction_part == 0) break; /* Truncate trailing 0's */ - } -} -#ifndef UNITY_ROUND_TIES_AWAY_FROM_ZERO -/* If rounds up && remainder 0.5 && result odd && below cutoff for double precision issues */ - #define ROUND_TIES_TO_EVEN(orig, num_int, num) \ - if (num_int > (num) && (num) - (num_int-1) <= 0.5 && (num_int & 1) == 1 && orig < 1e22) \ - num_int -= 1 /* => a tie to round down to even */ -#else - #define ROUND_TIES_TO_EVEN(orig, num_int, num) /* Remove macro */ -#endif - -/* Printing floating point numbers is hard. Some goals of this implementation: works for embedded - * systems, floats or doubles, and has a reasonable format. The key paper in this area, - * 'How to Print Floating-Point Numbers Accurately' by Steele & White, shows an approximation by - * scaling called Dragon 2. This code uses a similar idea. The other core algorithm uses casts and - * floating subtraction to give exact remainders after the decimal, to be scaled into an integer. - * Extra trailing 0's are excluded. The output defaults to rounding to nearest, ties to even. You - * can enable rounding ties away from zero. Note: UNITY_DOUBLE param can typedef to float or double - - * The old version required compiling in snprintf. For reference, with a similar format as now: - * char buf[19]; - * if (number > 4294967296.0 || -number > 4294967296.0) snprintf(buf, sizeof buf, "%.8e", number); - * else snprintf(buf, sizeof buf, "%.6f", number); - * UnityPrint(buf); - */ +/* This function prints a floating-point value in a format similar to + * printf("%.6g"). It can work with either single- or double-precision, + * but for simplicity, it prints only 6 significant digits in either case. + * Printing more than 6 digits accurately is hard (at least in the single- + * precision case) and isn't attempted here. */ void UnityPrintFloat(const UNITY_DOUBLE input_number) { - UNITY_DOUBLE number; + UNITY_DOUBLE number = input_number; - if (input_number < 0) + /* print minus sign (including for negative zero) */ + if (number < (double)0.0f || (number == (double)0.0f && (double)1.0f / number < (double)0.0f)) { UNITY_OUTPUT_CHAR('-'); - number = -input_number; - } else - { - number = input_number; + number = -number; } - if (isnan(number)) UnityPrint(UnityStrNaN); - else if (isinf(number)) UnityPrintLen(UnityStrInf, 3); - else if (number <= 0.0000005 && number > 0) UnityPrint("0.000000..."); /* Small number */ - else if (number < 4294967295.9999995) /* Rounded result fits in 32 bits, "%.6f" format */ + /* handle zero, NaN, and +/- infinity */ + if (number == (double)0.0f) UnityPrint("0"); + else if (isnan(number)) UnityPrint("nan"); + else if (isinf(number)) UnityPrint("inf"); + else { - const UNITY_INT32 divisor = 1000000/10; - UNITY_UINT32 integer_part = (UNITY_UINT32)number; - UNITY_INT32 fraction_part = (UNITY_INT32)((number - (UNITY_DOUBLE)integer_part)*1000000.0 + 0.5); - /* Double precision calculation gives best performance for six rounded decimal places */ - ROUND_TIES_TO_EVEN(number, fraction_part, (number - (UNITY_DOUBLE)integer_part)*1000000.0); + int exponent = 0; + int decimals, digits; + UNITY_INT32 n; + char buf[16]; + + /* scale up or down by powers of 10 */ + while (number < (double)(100000.0f / 1e6f)) { number *= (double)1e6f; exponent -= 6; } + while (number < (double)100000.0f) { number *= (double)10.0f; exponent--; } + while (number > (double)(1000000.0f * 1e6f)) { number /= (double)1e6f; exponent += 6; } + while (number > (double)1000000.0f) { number /= (double)10.0f; exponent++; } - if (fraction_part == 1000000) /* Carry across the decimal point */ + /* round to nearest integer */ + n = ((UNITY_INT32)(number + number) + 1) / 2; + if (n > 999999) { - fraction_part = 0; - integer_part += 1; + n = 100000; + exponent++; } - UnityPrintNumberUnsigned(integer_part); - UnityPrintDecimalAndNumberWithLeadingZeros(fraction_part, divisor); - } - else /* Number is larger, use exponential format of 9 digits, "%.8e" */ - { - const UNITY_INT32 divisor = 1000000000/10; - UNITY_INT32 integer_part; - UNITY_DOUBLE_TYPE divide = 10.0; - int exponent = 9; + /* determine where to place decimal point */ + decimals = (exponent <= 0 && exponent >= -9) ? -exponent : 5; + exponent += decimals; - while (number / divide >= 1000000000.0 - 0.5) + /* truncate trailing zeroes after decimal point */ + while (decimals > 0 && n % 10 == 0) { - divide *= 10; - exponent++; + n /= 10; + decimals--; + } + + /* build up buffer in reverse order */ + digits = 0; + while (n != 0 || digits < decimals + 1) + { + buf[digits++] = (char)('0' + n % 10); + n /= 10; + } + while (digits > 0) + { + if(digits == decimals) UNITY_OUTPUT_CHAR('.'); + UNITY_OUTPUT_CHAR(buf[--digits]); } - integer_part = (UNITY_INT32)(number / divide + 0.5); - /* Double precision calculation required for float, to produce 9 rounded digits */ - ROUND_TIES_TO_EVEN(number, integer_part, number / divide); - UNITY_OUTPUT_CHAR('0' + integer_part / divisor); - UnityPrintDecimalAndNumberWithLeadingZeros(integer_part % divisor, divisor / 10); - UNITY_OUTPUT_CHAR('e'); - UNITY_OUTPUT_CHAR('+'); - if (exponent < 10) UNITY_OUTPUT_CHAR('0'); - UnityPrintNumber(exponent); + /* print exponent if needed */ + if (exponent != 0) + { + UNITY_OUTPUT_CHAR('e'); + + if(exponent < 0) + { + UNITY_OUTPUT_CHAR('-'); + exponent = -exponent; + } + else + { + UNITY_OUTPUT_CHAR('+'); + } + + digits = 0; + while (exponent != 0 || digits < 2) + { + buf[digits++] = (char)('0' + exponent % 10); + exponent /= 10; + } + while (digits > 0) + { + UNITY_OUTPUT_CHAR(buf[--digits]); + } + } } } #endif /* ! UNITY_EXCLUDE_FLOAT_PRINT */ @@ -526,6 +551,45 @@ void UnityAssertEqualNumber(const UNITY_INT expected, } } +/*-----------------------------------------------*/ +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style) +{ + int failed = 0; + RETURN_IF_FAIL_OR_IGNORE; + + if (threshold == actual && compare & UNITY_EQUAL_TO) return; + if (threshold == actual) failed = 1; + + if ((style & UNITY_DISPLAY_RANGE_INT) == UNITY_DISPLAY_RANGE_INT) + { + if (actual > threshold && compare & UNITY_SMALLER_THAN) failed = 1; + if (actual < threshold && compare & UNITY_GREATER_THAN) failed = 1; + } + else /* UINT or HEX */ + { + if ((UNITY_UINT)actual > (UNITY_UINT)threshold && compare & UNITY_SMALLER_THAN) failed = 1; + if ((UNITY_UINT)actual < (UNITY_UINT)threshold && compare & UNITY_GREATER_THAN) failed = 1; + } + + if (failed) + { + UnityTestResultsFailBegin(lineNumber); + UnityPrint(UnityStrExpected); + UnityPrintNumberByStyle(actual, style); + if (compare & UNITY_GREATER_THAN) UnityPrint(UnityStrGt); + if (compare & UNITY_SMALLER_THAN) UnityPrint(UnityStrLt); + if (compare & UNITY_EQUAL_TO) UnityPrint(UnityStrOrEqual); + UnityPrintNumberByStyle(threshold, style); + UnityAddMsgIfSpecified(msg); + UNITY_FAIL_AND_BAIL; + } +} + #define UnityPrintPointlessAndBail() \ { \ UnityTestResultsFailBegin(lineNumber); \ @@ -1025,7 +1089,7 @@ void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, { UNITY_UINT32 i = 0; UNITY_UINT32 j = 0; - const char* exp = NULL; + const char* expd = NULL; const char* act = NULL; RETURN_IF_FAIL_OR_IGNORE; @@ -1048,7 +1112,7 @@ void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, if (flags != UNITY_ARRAY_TO_ARRAY) { - exp = (const char*)expected; + expd = (const char*)expected; } do @@ -1056,15 +1120,15 @@ void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, act = actual[j]; if (flags == UNITY_ARRAY_TO_ARRAY) { - exp = ((const char* const*)expected)[j]; + expd = ((const char* const*)expected)[j]; } /* if both pointers not null compare the strings */ - if (exp && act) + if (expd && act) { - for (i = 0; exp[i] || act[i]; i++) + for (i = 0; expd[i] || act[i]; i++) { - if (exp[i] != act[i]) + if (expd[i] != act[i]) { Unity.CurrentTestFailed = 1; break; @@ -1073,7 +1137,7 @@ void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, } else { /* handle case of one pointers being null (if both null, test should pass) */ - if (exp != act) + if (expd != act) { Unity.CurrentTestFailed = 1; } @@ -1087,7 +1151,7 @@ void UnityAssertEqualStringArray(UNITY_INTERNAL_PTR expected, UnityPrint(UnityStrElement); UnityPrintNumberUnsigned(j); } - UnityPrintExpectedAndActualStrings(exp, act); + UnityPrintExpectedAndActualStrings(expd, act); UnityAddMsgIfSpecified(msg); UNITY_FAIL_AND_BAIL; } @@ -1262,17 +1326,6 @@ void UnityIgnore(const char* msg, const UNITY_LINE_TYPE line) UNITY_IGNORE_AND_BAIL; } -/*-----------------------------------------------*/ -#if defined(UNITY_WEAK_ATTRIBUTE) - UNITY_WEAK_ATTRIBUTE void setUp(void) { } - UNITY_WEAK_ATTRIBUTE void tearDown(void) { } -#elif defined(UNITY_WEAK_PRAGMA) - #pragma weak setUp - void setUp(void) { } - #pragma weak tearDown - void tearDown(void) { } -#endif - /*-----------------------------------------------*/ void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) { diff --git a/tests/unity/src/unity.h b/tests/unity/src/unity.h index 643423839..32ff0e6df 100644 --- a/tests/unity/src/unity.h +++ b/tests/unity/src/unity.h @@ -15,9 +15,43 @@ extern "C" #include "unity_internals.h" +/*------------------------------------------------------- + * Test Setup / Teardown + *-------------------------------------------------------*/ + +/* These functions are intended to be called before and after each test. */ void setUp(void); void tearDown(void); +/* These functions are intended to be called at the beginning and end of an + * entire test suite. suiteTearDown() is passed the number of tests that + * failed, and its return value becomes the exit code of main(). */ +void suiteSetUp(void); +int suiteTearDown(int num_failures); + +/* If the compiler supports it, the following block provides stub + * implementations of the above functions as weak symbols. Note that on + * some platforms (MinGW for example), weak function implementations need + * to be in the same translation unit they are called from. This can be + * achieved by defining UNITY_INCLUDE_SETUP_STUBS before including unity.h. */ +#ifdef UNITY_INCLUDE_SETUP_STUBS + #ifdef UNITY_WEAK_ATTRIBUTE + UNITY_WEAK_ATTRIBUTE void setUp(void) { } + UNITY_WEAK_ATTRIBUTE void tearDown(void) { } + UNITY_WEAK_ATTRIBUTE void suiteSetUp(void) { } + UNITY_WEAK_ATTRIBUTE int suiteTearDown(int num_failures) { return num_failures; } + #elif defined(UNITY_WEAK_PRAGMA) + #pragma weak setUp + void setUp(void) { } + #pragma weak tearDown + void tearDown(void) { } + #pragma weak suiteSetUp + void suiteSetUp(void) { } + #pragma weak suiteTearDown + int suiteTearDown(int num_failures) { return num_failures; } + #endif +#endif + /*------------------------------------------------------- * Configuration Options *------------------------------------------------------- @@ -114,6 +148,71 @@ void tearDown(void); #define TEST_ASSERT_BIT_HIGH(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, NULL) #define TEST_ASSERT_BIT_LOW(bit, actual) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, NULL) +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_THAN(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_THAN_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_GREATER_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) + +#define TEST_ASSERT_LESS_OR_EQUAL(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, NULL) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64(threshold, actual) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, NULL) + /* Integer Ranges (of all sizes) */ #define TEST_ASSERT_INT_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, NULL) #define TEST_ASSERT_INT8_WITHIN(delta, expected, actual) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, NULL) @@ -241,6 +340,71 @@ void tearDown(void); #define TEST_ASSERT_BIT_HIGH_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(-1), (actual), __LINE__, (message)) #define TEST_ASSERT_BIT_LOW_MESSAGE(bit, actual, message) UNITY_TEST_ASSERT_BITS(((UNITY_UINT32)1 << (bit)), (UNITY_UINT32)(0), (actual), __LINE__, (message)) +/* Integer Greater Than/ Less Than (of all sizes) */ +#define TEST_ASSERT_GREATER_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_THAN_HEX64((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_THAN_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_THAN_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_GREATER_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64((threshold), (actual), __LINE__, (message)) + +#define TEST_ASSERT_LESS_OR_EQUAL_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_INT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_UINT64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX8_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX16_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX32_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX32((threshold), (actual), __LINE__, (message)) +#define TEST_ASSERT_LESS_OR_EQUAL_HEX64_MESSAGE(threshold, actual, message) UNITY_TEST_ASSERT_SMALLER_THAN_HEX64((threshold), (actual), __LINE__, (message)) + /* Integer Ranges (of all sizes) */ #define TEST_ASSERT_INT_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT_WITHIN((delta), (expected), (actual), __LINE__, (message)) #define TEST_ASSERT_INT8_WITHIN_MESSAGE(delta, expected, actual, message) UNITY_TEST_ASSERT_INT8_WITHIN((delta), (expected), (actual), __LINE__, (message)) @@ -295,7 +459,7 @@ void tearDown(void); #define TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT16((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT32((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_UINT64_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_UINT64((expected), (actual), (num_elements), __LINE__, (message)) -#define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) +#define TEST_ASSERT_EACH_EQUAL_HEX_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_HEX8_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX8((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_HEX16_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX16((expected), (actual), (num_elements), __LINE__, (message)) #define TEST_ASSERT_EACH_EQUAL_HEX32_MESSAGE(expected, actual, num_elements, message) UNITY_TEST_ASSERT_EACH_EQUAL_HEX32((expected), (actual), (num_elements), __LINE__, (message)) diff --git a/tests/unity/src/unity_internals.h b/tests/unity/src/unity_internals.h index 2cc19734b..f78cebace 100644 --- a/tests/unity/src/unity_internals.h +++ b/tests/unity/src/unity_internals.h @@ -244,8 +244,8 @@ typedef UNITY_FLOAT_TYPE UNITY_FLOAT; #define UNITY_OUTPUT_CHAR(a) (void)putchar(a) #else /* If defined as something else, make sure we declare it here so it's ready for use */ - #ifndef UNITY_OMIT_OUTPUT_CHAR_HEADER_DECLARATION -extern void UNITY_OUTPUT_CHAR(int); + #ifdef UNITY_OUTPUT_CHAR_HEADER_DECLARATION +extern void UNITY_OUTPUT_CHAR_HEADER_DECLARATION; #endif #endif @@ -253,22 +253,22 @@ extern void UNITY_OUTPUT_CHAR(int); #ifdef UNITY_USE_FLUSH_STDOUT /* We want to use the stdout flush utility */ #include -#define UNITY_OUTPUT_FLUSH (void)fflush(stdout) +#define UNITY_OUTPUT_FLUSH() (void)fflush(stdout) #else /* We've specified nothing, therefore flush should just be ignored */ -#define UNITY_OUTPUT_FLUSH +#define UNITY_OUTPUT_FLUSH() #endif #else /* We've defined flush as something else, so make sure we declare it here so it's ready for use */ -#ifndef UNITY_OMIT_OUTPUT_FLUSH_HEADER_DECLARATION -extern void UNITY_OUTPUT_FLUSH(void); +#ifdef UNITY_OUTPUT_FLUSH_HEADER_DECLARATION +extern void UNITY_OMIT_OUTPUT_FLUSH_HEADER_DECLARATION; #endif #endif #ifndef UNITY_OUTPUT_FLUSH #define UNITY_FLUSH_CALL() #else -#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH +#define UNITY_FLUSH_CALL() UNITY_OUTPUT_FLUSH() #endif #ifndef UNITY_PRINT_EOL @@ -299,7 +299,7 @@ extern void UNITY_OUTPUT_FLUSH(void); * Language Features Available *-------------------------------------------------------*/ #if !defined(UNITY_WEAK_ATTRIBUTE) && !defined(UNITY_WEAK_PRAGMA) -# ifdef __GNUC__ /* includes clang */ +# if defined(__GNUC__) || defined(__ghs__) /* __GNUC__ includes clang */ # if !(defined(__WIN32__) && defined(__clang__)) && !defined(__TMS470__) # define UNITY_WEAK_ATTRIBUTE __attribute__((weak)) # endif @@ -350,6 +350,15 @@ UNITY_DISPLAY_STYLE_UINT = sizeof(unsigned) + UNITY_DISPLAY_RANGE_UINT, UNITY_DISPLAY_STYLE_UNKNOWN } UNITY_DISPLAY_STYLE_T; +typedef enum +{ + UNITY_EQUAL_TO = 1, + UNITY_GREATER_THAN = 2, + UNITY_GREATER_OR_EQUAL = 2 + UNITY_EQUAL_TO, + UNITY_SMALLER_THAN = 4, + UNITY_SMALLER_OR_EQUAL = 4 + UNITY_EQUAL_TO +} UNITY_COMPARISON_T; + #ifndef UNITY_EXCLUDE_FLOAT typedef enum UNITY_FLOAT_TRAIT { @@ -453,6 +462,13 @@ void UnityAssertEqualNumber(const UNITY_INT expected, const UNITY_LINE_TYPE lineNumber, const UNITY_DISPLAY_STYLE_T style); +void UnityAssertGreaterOrLessOrEqualNumber(const UNITY_INT threshold, + const UNITY_INT actual, + const UNITY_COMPARISON_T compare, + const char *msg, + const UNITY_LINE_TYPE lineNumber, + const UNITY_DISPLAY_STYLE_T style); + void UnityAssertEqualIntArray(UNITY_INTERNAL_PTR expected, UNITY_INTERNAL_PTR actual, const UNITY_UINT32 num_elements, @@ -650,6 +666,54 @@ int UnityTestMatches(void); #define UNITY_TEST_ASSERT_EQUAL_HEX32(expected, actual, line, message) UnityAssertEqualNumber((UNITY_INT)(UNITY_INT32)(expected), (UNITY_INT)(UNITY_INT32)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) #define UNITY_TEST_ASSERT_BITS(mask, expected, actual, line, message) UnityAssertBits((UNITY_INT)(mask), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line)) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT8 )(threshold), (UNITY_INT)(UNITY_INT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT16)(threshold), (UNITY_INT)(UNITY_INT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_INT32)(threshold), (UNITY_INT)(UNITY_INT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT32) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX8(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT8 )(threshold), (UNITY_INT)(UNITY_UINT8 )(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX8) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX16(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT16)(threshold), (UNITY_INT)(UNITY_UINT16)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX16) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX32(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(UNITY_UINT32)(threshold), (UNITY_INT)(UNITY_UINT32)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX32) + #define UNITY_TEST_ASSERT_INT_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT) #define UNITY_TEST_ASSERT_INT8_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT8 )(delta), (UNITY_INT)(UNITY_INT8 )(expected), (UNITY_INT)(UNITY_INT8 )(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT8) #define UNITY_TEST_ASSERT_INT16_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((UNITY_UINT16)(delta), (UNITY_INT)(UNITY_INT16)(expected), (UNITY_INT)(UNITY_INT16)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT16) @@ -704,12 +768,24 @@ int UnityTestMatches(void); #define UNITY_TEST_ASSERT_EQUAL_INT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_UINT64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) #define UNITY_TEST_ASSERT_EQUAL_HEX64_ARRAY(expected, actual, num_elements, line, message) UnityAssertEqualIntArray((UNITY_INTERNAL_PTR)(expected), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_ARRAY) -#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_ARRAY) +#define UNITY_TEST_ASSERT_EACH_EQUAL_INT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_UINT64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_UINT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64, UNITY_ARRAY_TO_VAL) +#define UNITY_TEST_ASSERT_EACH_EQUAL_HEX64(expected, actual, num_elements, line, message) UnityAssertEqualIntArray(UnityNumToPtr((UNITY_INT)(UNITY_INT64)expected, 8), (UNITY_INTERNAL_PTR)(actual), (UNITY_UINT32)(num_elements), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64, UNITY_ARRAY_TO_VAL) #define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) #define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) #define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UnityAssertNumbersWithin((delta), (UNITY_INT)(expected), (UNITY_INT)(actual), (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_GREATER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_THAN, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_INT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_UINT64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UnityAssertGreaterOrLessOrEqualNumber((UNITY_INT)(threshold), (UNITY_INT)(actual), UNITY_SMALLER_OR_EQUAL, (message), (UNITY_LINE_TYPE)(line), UNITY_DISPLAY_STYLE_HEX64) #else #define UNITY_TEST_ASSERT_EQUAL_INT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_EQUAL_UINT64(expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) @@ -720,6 +796,18 @@ int UnityTestMatches(void); #define UNITY_TEST_ASSERT_INT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_UINT64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #define UNITY_TEST_ASSERT_HEX64_WITHIN(delta, expected, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_GREATER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_THAN_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_INT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_UINT64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) +#define UNITY_TEST_ASSERT_SMALLER_OR_EQUAL_HEX64(threshold, actual, line, message) UNITY_TEST_FAIL((UNITY_LINE_TYPE)(line), UnityStrErr64) #endif #ifdef UNITY_EXCLUDE_FLOAT diff --git a/tests/unity/test/Makefile b/tests/unity/test/Makefile index 03b59bc4c..c2710f1f1 100644 --- a/tests/unity/test/Makefile +++ b/tests/unity/test/Makefile @@ -14,7 +14,10 @@ CFLAGS += -Wbad-function-cast -Wcast-qual -Wold-style-definition -Wshadow -Wstri #DEBUG = -O0 -g CFLAGS += $(DEBUG) DEFINES = -D UNITY_OUTPUT_CHAR=putcharSpy -DEFINES += -D UNITY_SUPPORT_64 -D UNITY_INCLUDE_DOUBLE +DEFINES += -D UNITY_OUTPUT_CHAR_HEADER_DECLARATION=putcharSpy\(int\) +DEFINES += $(UNITY_SUPPORT_64) $(UNITY_INCLUDE_DOUBLE) +UNITY_SUPPORT_64 = -D UNITY_SUPPORT_64 +UNITY_INCLUDE_DOUBLE = -D UNITY_INCLUDE_DOUBLE SRC = ../src/unity.c tests/testunity.c build/testunityRunner.c INC_DIR = -I ../src COV_FLAGS = -fprofile-arcs -ftest-coverage -I ../../src diff --git a/tests/unity/test/rakefile b/tests/unity/test/rakefile index 4a2f3d2c1..f05fb7a97 100644 --- a/tests/unity/test/rakefile +++ b/tests/unity/test/rakefile @@ -26,7 +26,7 @@ task :prepare_for_tests => TEMP_DIRS include RakefileHelpers -# Load proper GCC as defult configuration +# Load proper GCC as default configuration DEFAULT_CONFIG_FILE = 'gcc_auto_stdint.yml' configure_toolchain(DEFAULT_CONFIG_FILE) diff --git a/tests/unity/test/rakefile_helper.rb b/tests/unity/test/rakefile_helper.rb index be5bf3ea2..1fd60c5fd 100644 --- a/tests/unity/test/rakefile_helper.rb +++ b/tests/unity/test/rakefile_helper.rb @@ -91,7 +91,7 @@ def build_compiler_fields(inject_defines) defines = if $cfg['compiler']['defines']['items'].nil? '' else - squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items'] + ['UNITY_OUTPUT_CHAR=putcharSpy'] + inject_defines) + squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items'] + ['UNITY_OUTPUT_CHAR=putcharSpy'] + ['UNITY_OUTPUT_CHAR_HEADER_DECLARATION=putcharSpy\(int\)'] + inject_defines) end options = squash('', $cfg['compiler']['options']) includes = squash($cfg['compiler']['includes']['prefix'], $cfg['compiler']['includes']['items']) diff --git a/tests/unity/test/tests/testunity.c b/tests/unity/test/tests/testunity.c index 3555fb2d6..af06647f8 100644 --- a/tests/unity/test/tests/testunity.c +++ b/tests/unity/test/tests/testunity.c @@ -764,8 +764,9 @@ void testNotEqualBitsLow(void) EXPECT_ABORT_BEGIN TEST_ASSERT_BITS_LOW(v0, v1); VERIFY_FAILS_END - } + + void testEqualShorts(void) { short v0, v1; @@ -1305,6 +1306,415 @@ void testINT8sNotWithinDeltaAndCustomMessage(void) VERIFY_FAILS_END } + +//----------------- +void testGreaterThan(void) +{ + UNITY_INT v0, v1; + UNITY_INT *p0, *p1; + + v0 = 0; + v1 = 1; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN(v0, v1); + TEST_ASSERT_GREATER_THAN(*p0, v1); + TEST_ASSERT_GREATER_THAN(v0, *p1); + TEST_ASSERT_GREATER_THAN(*p0, *p1); +} + +void testGreaterThanINT(void) +{ + UNITY_INT v0, v1; + UNITY_INT *p0, *p1; + + v0 = 302; + v1 = 3334; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_INT(v0, v1); + TEST_ASSERT_GREATER_THAN_INT(*p0, v1); + TEST_ASSERT_GREATER_THAN_INT(v0, *p1); + TEST_ASSERT_GREATER_THAN_INT(*p0, *p1); +} + + +void testGreaterThanINT8(void) +{ + UNITY_INT8 v0, v1; + UNITY_INT8 *p0, *p1; + + v0 = -128; + v1 = 127; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_INT8(v0, v1); + TEST_ASSERT_GREATER_THAN_INT8(*p0, v1); + TEST_ASSERT_GREATER_THAN_INT8(v0, *p1); + TEST_ASSERT_GREATER_THAN_INT8(*p0, *p1); +} + +void testGreaterThanINT16(void) +{ + UNITY_INT16 v0, v1; + UNITY_INT16 *p0, *p1; + + v0 = -32768; + v1 = 32767; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_INT16(v0, v1); + TEST_ASSERT_GREATER_THAN_INT16(*p0, v1); + TEST_ASSERT_GREATER_THAN_INT16(v0, *p1); + TEST_ASSERT_GREATER_THAN_INT16(*p0, *p1); +} + +void testGreaterThanINT32(void) +{ + UNITY_INT32 v0, v1; + UNITY_INT32 *p0, *p1; + + v0 = -214783648; + v1 = 214783647; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_INT32(v0, v1); + TEST_ASSERT_GREATER_THAN_INT32(*p0, v1); + TEST_ASSERT_GREATER_THAN_INT32(v0, *p1); + TEST_ASSERT_GREATER_THAN_INT32(*p0, *p1); +} + +void testGreaterThanUINT(void) +{ + UNITY_UINT v0, v1; + UNITY_UINT *p0, *p1; + + v0 = 0; + v1 = 1; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_UINT(v0, v1); + TEST_ASSERT_GREATER_THAN_UINT(*p0, v1); + TEST_ASSERT_GREATER_THAN_UINT(v0, *p1); + TEST_ASSERT_GREATER_THAN_UINT(*p0, *p1); +} + + +void testGreaterThanUINT8(void) +{ + UNITY_UINT8 v0, v1; + UNITY_UINT8 *p0, *p1; + + v0 = 0; + v1 = 255; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_UINT8(v0, v1); + TEST_ASSERT_GREATER_THAN_UINT8(*p0, v1); + TEST_ASSERT_GREATER_THAN_UINT8(v0, *p1); + TEST_ASSERT_GREATER_THAN_UINT8(*p0, *p1); +} + +void testGreaterThanUINT16(void) +{ + UNITY_UINT16 v0, v1; + UNITY_UINT16 *p0, *p1; + + v0 = 0; + v1 = 65535; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_UINT16(v0, v1); + TEST_ASSERT_GREATER_THAN_UINT16(*p0, v1); + TEST_ASSERT_GREATER_THAN_UINT16(v0, *p1); + TEST_ASSERT_GREATER_THAN_UINT16(*p0, *p1); +} + +void testGreaterThanUINT32(void) +{ + UNITY_UINT32 v0, v1; + UNITY_UINT32 *p0, *p1; + + v0 = 0; + v1 = 4294967295; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_UINT32(v0, v1); + TEST_ASSERT_GREATER_THAN_UINT32(*p0, v1); + TEST_ASSERT_GREATER_THAN_UINT32(v0, *p1); + TEST_ASSERT_GREATER_THAN_UINT32(*p0, *p1); +} + +void testGreaterThanHEX8(void) +{ + UNITY_UINT8 v0, v1; + UNITY_UINT8 *p0, *p1; + + v0 = 0x00; + v1 = 0xFF; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_HEX8(v0, v1); + TEST_ASSERT_GREATER_THAN_HEX8(*p0, v1); + TEST_ASSERT_GREATER_THAN_HEX8(v0, *p1); + TEST_ASSERT_GREATER_THAN_HEX8(*p0, *p1); +} + +void testGreaterThanHEX16(void) +{ + UNITY_UINT16 v0, v1; + UNITY_UINT16 *p0, *p1; + + v0 = 0x0000; + v1 = 0xFFFF; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_HEX16(v0, v1); + TEST_ASSERT_GREATER_THAN_HEX16(*p0, v1); + TEST_ASSERT_GREATER_THAN_HEX16(v0, *p1); + TEST_ASSERT_GREATER_THAN_HEX16(*p0, *p1); +} + +void testGreaterThanHEX32(void) +{ + UNITY_UINT32 v0, v1; + UNITY_UINT32 *p0, *p1; + + v0 = 0x00000000; + v1 = 0xFFFFFFFF; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_GREATER_THAN_HEX32(v0, v1); + TEST_ASSERT_GREATER_THAN_HEX32(*p0, v1); + TEST_ASSERT_GREATER_THAN_HEX32(v0, *p1); + TEST_ASSERT_GREATER_THAN_HEX32(*p0, *p1); +} + + +void testNotGreaterThan(void) +{ + EXPECT_ABORT_BEGIN + TEST_ASSERT_GREATER_THAN(0, -1); + VERIFY_FAILS_END +} + +void testLessThan(void) +{ + UNITY_INT v0, v1; + UNITY_INT *p0, *p1; + + v0 = 0; + v1 = -1; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN(v0, v1); + TEST_ASSERT_LESS_THAN(*p0, v1); + TEST_ASSERT_LESS_THAN(v0, *p1); + TEST_ASSERT_LESS_THAN(*p0, *p1); +} + +void testLessThanINT(void) +{ + UNITY_INT v0, v1; + UNITY_INT *p0, *p1; + + v0 = 3334; + v1 = 302; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_INT(v0, v1); + TEST_ASSERT_LESS_THAN_INT(*p0, v1); + TEST_ASSERT_LESS_THAN_INT(v0, *p1); + TEST_ASSERT_LESS_THAN_INT(*p0, *p1); +} + + +void testLessThanINT8(void) +{ + UNITY_INT8 v0, v1; + UNITY_INT8 *p0, *p1; + + v0 = 127; + v1 = -128; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_INT8(v0, v1); + TEST_ASSERT_LESS_THAN_INT8(*p0, v1); + TEST_ASSERT_LESS_THAN_INT8(v0, *p1); + TEST_ASSERT_LESS_THAN_INT8(*p0, *p1); +} + +void testLessThanINT16(void) +{ + UNITY_INT16 v0, v1; + UNITY_INT16 *p0, *p1; + + v0 = 32767; + v1 = -32768; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_INT16(v0, v1); + TEST_ASSERT_LESS_THAN_INT16(*p0, v1); + TEST_ASSERT_LESS_THAN_INT16(v0, *p1); + TEST_ASSERT_LESS_THAN_INT16(*p0, *p1); +} + +void testLessThanINT32(void) +{ + UNITY_INT32 v0, v1; + UNITY_INT32 *p0, *p1; + + v0 = 214783647; + v1 = -214783648; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_INT32(v0, v1); + TEST_ASSERT_LESS_THAN_INT32(*p0, v1); + TEST_ASSERT_LESS_THAN_INT32(v0, *p1); + TEST_ASSERT_LESS_THAN_INT32(*p0, *p1); +} + +void testLessThanUINT(void) +{ + UNITY_UINT v0, v1; + UNITY_UINT *p0, *p1; + + v0 = 1; + v1 = 0; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_UINT(v0, v1); + TEST_ASSERT_LESS_THAN_UINT(*p0, v1); + TEST_ASSERT_LESS_THAN_UINT(v0, *p1); + TEST_ASSERT_LESS_THAN_UINT(*p0, *p1); +} + + +void testLessThanUINT8(void) +{ + UNITY_UINT8 v0, v1; + UNITY_UINT8 *p0, *p1; + + v0 = 255; + v1 = 0; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_UINT8(v0, v1); + TEST_ASSERT_LESS_THAN_UINT8(*p0, v1); + TEST_ASSERT_LESS_THAN_UINT8(v0, *p1); + TEST_ASSERT_LESS_THAN_UINT8(*p0, *p1); +} + +void testLessThanUINT16(void) +{ + UNITY_UINT16 v0, v1; + UNITY_UINT16 *p0, *p1; + + v0 = 65535; + v1 = 0; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_UINT16(v0, v1); + TEST_ASSERT_LESS_THAN_UINT16(*p0, v1); + TEST_ASSERT_LESS_THAN_UINT16(v0, *p1); + TEST_ASSERT_LESS_THAN_UINT16(*p0, *p1); +} + +void testLessThanUINT32(void) +{ + UNITY_UINT32 v0, v1; + UNITY_UINT32 *p0, *p1; + + v0 = 4294967295; + v1 = 0; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_UINT32(v0, v1); + TEST_ASSERT_LESS_THAN_UINT32(*p0, v1); + TEST_ASSERT_LESS_THAN_UINT32(v0, *p1); + TEST_ASSERT_LESS_THAN_UINT32(*p0, *p1); +} + +void testLessThanHEX8(void) +{ + UNITY_UINT8 v0, v1; + UNITY_UINT8 *p0, *p1; + + v0 = 0xFF; + v1 = 0x00; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_HEX8(v0, v1); + TEST_ASSERT_LESS_THAN_HEX8(*p0, v1); + TEST_ASSERT_LESS_THAN_HEX8(v0, *p1); + TEST_ASSERT_LESS_THAN_HEX8(*p0, *p1); +} + +void testLessThanHEX16(void) +{ + UNITY_UINT16 v0, v1; + UNITY_UINT16 *p0, *p1; + + v0 = 0xFFFF; + v1 = 0x0000; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_HEX16(v0, v1); + TEST_ASSERT_LESS_THAN_HEX16(*p0, v1); + TEST_ASSERT_LESS_THAN_HEX16(v0, *p1); + TEST_ASSERT_LESS_THAN_HEX16(*p0, *p1); +} + +void testLessThanHEX32(void) +{ + UNITY_UINT32 v0, v1; + UNITY_UINT32 *p0, *p1; + + v0 = 0xFFFFFFFF; + v1 = 0x00000000; + p0 = &v0; + p1 = &v1; + + TEST_ASSERT_LESS_THAN_HEX32(v0, v1); + TEST_ASSERT_LESS_THAN_HEX32(*p0, v1); + TEST_ASSERT_LESS_THAN_HEX32(v0, *p1); + TEST_ASSERT_LESS_THAN_HEX32(*p0, *p1); +} + + +void testNotLessThan(void) +{ + EXPECT_ABORT_BEGIN + TEST_ASSERT_LESS_THAN(0, 1); + VERIFY_FAILS_END +} + + + +//----------------- void testEqualStrings(void) { const char *testString = "foo"; @@ -4056,61 +4466,46 @@ void testFloatPrinting(void) #if defined(UNITY_EXCLUDE_FLOAT_PRINT) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.0", 0.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.000000...", 0.000000499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.000001", 0.00000050000005f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.100469499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0", 0.9999995f); /*Rounding to int place*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0", 1.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.25", 1.25f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("7.999999", 7.999999f); /*Not rounding*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("16.000002", 16.000002f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("16.000004", 16.000004f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("16.000006", 16.000006f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4294967040.0", 4294967040.0f); /*Last full print integer*/ - - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.0", -0.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.000000...",-0.000000499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.000001", -0.00000050000005f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.100469499f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.0", -0.9999995f); /*Rounding to int place*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.0", -1.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.25", -1.25f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.999999", -7.999999f); /*Not rounding*/ - TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.000002", -16.000002f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.000004", -16.000004f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.000006", -16.000006f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4294967040.0",-4294967040.0f); /*Last full print integer*/ - - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("5.0e+09", 5000000000.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("8.0e+09", 8.0e+09f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("8.3099991e+09", 8309999104.0f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 1.0e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 10000000000.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0", 0.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.99e-07", 0.000000499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("5e-07", 0.00000050000005f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.100469499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1", 0.9999995f); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("1", 1.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.25", 1.25f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("7.99999", 7.99999f); /*Not rounding*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.0002", 16.0002f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.0004", 16.0004f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("16.0006", 16.0006f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("999999", 999999.0f); /*Last full print integer*/ + + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0", -0.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.99e-07", -0.000000499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-5e-07", -0.00000050000005f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.100469499f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1", -0.9999995f); /*Rounding to int place*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1", -1.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.25", -1.25f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.99999", -7.99999f); /*Not rounding*/ + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.0002", -16.0002f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.0004", -16.0004f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-16.0006", -16.0006f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-999999", -999999.0f); /*Last full print integer*/ + + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.29497e+09", 4294967296.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("5e+09", 5000000000.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("8e+09", 8.0e+09f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("8.31e+09", 8309999104.0f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 1.0e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 10000000000.0f); /* Some compilers have trouble with inexact float constants, a float cast works generally */ - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.00005499e+10", (float)1.000055e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.10000006e+38", (float)1.10000005e+38f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.63529943e+10", 1.63529943e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("3.40282347e+38", 3.40282346638e38f); - - TEST_ASSERT_EQUAL_PRINT_FLOATING("-1.0e+10", -1.0e+10f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-3.40282347e+38",-3.40282346638e38f); -#endif -} + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.00005e+10", (float)1.000054e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.1e+38", (float)1.10000005e+38f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1.6353e+10", 1.63529943e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("3.40282e+38", 3.40282346638e38f); -void testFloatPrintingRoundTiesToEven(void) -{ -#if defined(UNITY_EXCLUDE_FLOAT_PRINT) || !defined(USING_OUTPUT_SPY) - TEST_IGNORE(); -#else - #ifdef UNITY_ROUND_TIES_AWAY_FROM_ZERO - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.007813", 0.0078125f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.976563", 0.9765625f); - #else /* Default to Round ties to even */ - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.007182", 0.0071825f); - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.976562", 0.9765625f); - #endif + TEST_ASSERT_EQUAL_PRINT_FLOATING("-1e+10", -1.0e+10f); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-3.40282e+38", -3.40282346638e38f); #endif } @@ -4119,105 +4514,69 @@ void testFloatPrintingInfinityAndNaN(void) #if defined(UNITY_EXCLUDE_FLOAT_PRINT) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else - TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0f / f_zero); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -1.0f / f_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("inf", 1.0f / f_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-inf", -1.0f / f_zero); - TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", 0.0f / f_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("nan", 0.0f / f_zero); #endif } #if defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) && defined(USING_OUTPUT_SPY) -static void AllFloatPrinting_LessThan32Bits(void) +static void printFloatValue(float f) { char expected[18]; - union { float f_value; int32_t int_value; } u; - /* Float representations are laid out in integer order, walk up the list */ - for (u.f_value = 0.00000050000005f; u.f_value <= 4294967040.0f; u.int_value += 1) - { - startPutcharSpy(); - - UnityPrintFloat(u.f_value); /*1.5x as fast as sprintf 5e-7f - 0.01f, 20s vs 30s*/ - int len = sprintf(expected, "%.6f", u.f_value); - - while (expected[len - 1] == '0' && expected[len - 2] != '.') { len--; } - expected[len] = '\0'; /* delete trailing 0's */ - - if (strcmp(expected, getBufferPutcharSpy()) != 0) - { - double six_digits = ((double)u.f_value - (uint32_t)u.f_value)*1000000.0; - /* Not a tie (remainder != 0.5) => Can't explain the different strings */ - if (six_digits - (uint32_t)six_digits != 0.5) - { - /* Fail with diagnostic printing */ - TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, u.f_value); - } - } - } -} + char expected_lower[18]; + char expected_higher[18]; -/* Compared to perfect, floats are occasionally rounded wrong. It doesn't affect - * correctness, though. Two examples (of 13 total found during testing): - * Printed: 6.19256349e+20, Exact: 619256348499999981568.0f <= Eliminated by ROUND_TIES_TO_EVEN - * Printed: 2.19012272e+35, Exact: 219012271499999993621766990196637696.0f */ -static void AllFloatPrinting_Larger(const float start, const float end) -{ - unsigned int wrong = 0; - char expected[18]; - union { float f_value; int32_t int_value; } u; - for (u.f_value = start; u.f_value <= end; u.int_value += 1) - { - startPutcharSpy(); + startPutcharSpy(); - UnityPrintFloat(u.f_value); /*Twice as fast as sprintf 2**32-1e12, 10s vs 21s*/ - sprintf(expected, "%.8e", u.f_value); + UnityPrintFloat(f); - int len = 11 - 1; /* 11th char is 'e' in exponential format */ - while (expected[len - 1] == '0' && expected[len - 2] != '.') { len --; } - if (expected[14] != '\0') memmove(&expected[12], &expected[13], 3); /* Two char exponent */ - memmove(&expected[len], &expected[11 - 1], sizeof "e+09"); /* 5 char length */ + sprintf(expected, "%.6g", f); - if (strcmp(expected, getBufferPutcharSpy()) != 0) - { - wrong++; - /* endPutcharSpy(); UnityPrint("Expected "); UnityPrint(expected); - UnityPrint(" Was "); UnityPrint(getBufferPutcharSpy()); UNITY_OUTPUT_CHAR('\n'); */ + /* We print all NaN's as "nan", not "-nan" */ + if(strcmp(expected, "-nan") == 0) strcpy(expected, "nan"); - if (wrong > 10 || (wrong > 3 && end <= 1e25f)) - TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, u.f_value); - /* Empirical values from the current routine, don't be worse when making changes */ - } + /* Allow for rounding differences in last digit */ + double lower = (double)f * 0.9999995; + double higher = (double)f * 1.0000005; + + if (isfinite(lower)) sprintf(expected_lower, "%.6g", lower); else strcpy(expected_lower, expected); + if (isfinite(higher)) sprintf(expected_higher, "%.6g", higher); else strcpy(expected_higher, expected); + + if (strcmp(expected, getBufferPutcharSpy()) != 0 && + strcmp(expected_lower, getBufferPutcharSpy()) != 0 && + strcmp(expected_higher, getBufferPutcharSpy()) != 0) + { + /* Fail with diagnostic printing */ + TEST_ASSERT_EQUAL_PRINT_FLOATING(expected, f); } } #endif -/* Exhaustive testing of all float values we differentiate when printing. Doubles - * are not explored here -- too many. These tests confirm that the routine works - * for all floats > 5e-7, positives only. Off by default due to test time. - * Compares Unity's routine to your sprintf() C lib, tested to pass on 3 platforms. - * Part1 takes a long time, around 3 minutes compiled with -O2 - * Runs through all floats from 0.000001 - 2**32, ~300 million values */ -void testAllFloatPrintingPart1_LessThan32Bits(void) +void testFloatPrintingRandomSamples(void) { -#if defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) && defined(USING_OUTPUT_SPY) - AllFloatPrinting_LessThan32Bits(); +#if !defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) || !defined(USING_OUTPUT_SPY) + TEST_IGNORE(); #else - TEST_IGNORE(); /* Ignore one of three */ -#endif -} + union { float f_value; uint32_t int_value; } u; -/* Test takes a long time, around 3.5 minutes compiled with -O2, try ~500 million values */ -void testAllFloatPrintingPart2_Larger(void) -{ -#if defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) && defined(USING_OUTPUT_SPY) - AllFloatPrinting_Larger(4294967296.0f, 1e25f); -#endif -} + /* These values are not covered by the MINSTD generator */ + u.int_value = 0x00000000; printFloatValue(u.f_value); + u.int_value = 0x80000000; printFloatValue(u.f_value); + u.int_value = 0x7fffffff; printFloatValue(u.f_value); + u.int_value = 0xffffffff; printFloatValue(u.f_value); -/* Test takes a long time, around 3.5 minutes compiled with -O2, try ~500 million values */ -void testAllFloatPrintingPart3_LargerStill(void) -{ -#if defined(UNITY_TEST_ALL_FLOATS_PRINT_OK) && defined(USING_OUTPUT_SPY) - AllFloatPrinting_Larger(1e25f, 3.40282347e+38f); + uint32_t a = 1; + for(int num_tested = 0; num_tested < 1000000; num_tested++) + { + /* MINSTD pseudo-random number generator */ + a = (uint32_t)(((uint64_t)a * 48271u) % 2147483647u); + + /* MINSTD does not set the highest bit; test both possibilities */ + u.int_value = a; printFloatValue(u.f_value); + u.int_value = a | 0x80000000; printFloatValue(u.f_value); + } #endif } @@ -4893,35 +5252,20 @@ void testDoublePrinting(void) #if defined(UNITY_EXCLUDE_FLOAT_PRINT) || defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else - TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.10046949999999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4294967295.999999", 4294967295.999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967295.9999995); - TEST_ASSERT_EQUAL_PRINT_FLOATING("4.2949673e+09", 4294967296.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 9999999995.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719925e+15", 9007199254740990.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("7.0e+100", 7.0e+100); - TEST_ASSERT_EQUAL_PRINT_FLOATING("3.0e+200", 3.0e+200); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.23456789e+300", 9.23456789e+300); + TEST_ASSERT_EQUAL_PRINT_FLOATING("0.100469", 0.10046949999999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.29497e+09", 4294967295.999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.29497e+09", 4294967295.9999995); + TEST_ASSERT_EQUAL_PRINT_FLOATING("4.29497e+09", 4294967296.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("1e+10", 9999999995.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.0072e+15", 9007199254740990.0); + TEST_ASSERT_EQUAL_PRINT_FLOATING("7e+100", 7.0e+100); + TEST_ASSERT_EQUAL_PRINT_FLOATING("3e+200", 3.0e+200); + TEST_ASSERT_EQUAL_PRINT_FLOATING("9.23457e+300", 9.23456789e+300); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.10046949999999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4294967295.999999", -4294967295.999999); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.2949673e+09", -4294967295.9999995); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-7.0e+100", -7.0e+100); -#endif -} - -void testDoublePrintingRoundTiesToEven(void) -{ -#if defined(UNITY_EXCLUDE_FLOAT_PRINT) || defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) - TEST_IGNORE(); -#else - #ifdef UNITY_ROUND_TIES_AWAY_FROM_ZERO - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.00000001e+10", 10000000050.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719925e+15", 9007199245000000.0); - #else /* Default to Round ties to even */ - TEST_ASSERT_EQUAL_PRINT_FLOATING("1.0e+10", 10000000050.0); - TEST_ASSERT_EQUAL_PRINT_FLOATING("9.00719924e+15", 9007199245000000.0); - #endif + TEST_ASSERT_EQUAL_PRINT_FLOATING("-0.100469", -0.10046949999999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.29497e+09", -4294967295.999999); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-4.29497e+09", -4294967295.9999995); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-7e+100", -7.0e+100); #endif } @@ -4930,10 +5274,10 @@ void testDoublePrintingInfinityAndNaN(void) #if defined(UNITY_EXCLUDE_FLOAT_PRINT) || defined(UNITY_EXCLUDE_DOUBLE) || !defined(USING_OUTPUT_SPY) TEST_IGNORE(); #else - TEST_ASSERT_EQUAL_PRINT_FLOATING("Inf", 1.0 / d_zero); - TEST_ASSERT_EQUAL_PRINT_FLOATING("-Inf", -1.0 / d_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("inf", 1.0 / d_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("-inf", -1.0 / d_zero); - TEST_ASSERT_EQUAL_PRINT_FLOATING("NaN", 0.0 / d_zero); + TEST_ASSERT_EQUAL_PRINT_FLOATING("nan", 0.0 / d_zero); #endif } diff --git a/tests/unity_setup.c b/tests/unity_setup.c new file mode 100644 index 000000000..1d828b0b3 --- /dev/null +++ b/tests/unity_setup.c @@ -0,0 +1,3 @@ +// msvc doesn't support weak-linking, so we need to define these functions. +void setUp(void) { } +void tearDown(void) { } diff --git a/valgrind.supp b/valgrind.supp new file mode 100644 index 000000000..79b55c674 --- /dev/null +++ b/valgrind.supp @@ -0,0 +1,6 @@ +{ + suppress_ld_on_armv7 + Memcheck:Cond + ... + obj:*/ld-*.so +}