Compare commits

...

62 Commits

Author SHA1 Message Date
1f7350658e Set version to 6.17.0 2020-10-19 11:35:09 +02:00
41132b701b Fixed error "no matching function for pgm_read<double>()" on AVR 2020-10-17 14:02:26 +02:00
712005219c Added filtering for MessagePack (closes #1298, closes #1394) 2020-10-13 09:40:39 +02:00
0bd17aff8a Coverage: use -O0 instead of -Og 2020-10-11 15:10:52 +02:00
f3f44d7812 Renamed StdStringAdapter to StlStringAdapter (internal use only) 2020-10-01 10:01:33 +02:00
8385d5fa3a Added wildcard key (*) for filters (closes #1309) 2020-09-28 21:11:38 +02:00
726f8be341 Added operator|(JsonVariantConst, JsonVariantConst) 2020-09-26 14:48:17 +02:00
fee029b86e Moved float tables to PROGMEM 2020-09-19 16:12:49 +02:00
c3504ddf0a Added tests for DeserializationError::f_str() 2020-09-18 19:06:21 +02:00
6a878ee444 Fixed error "No such file or directory WString.h" (fixes #1381) 2020-09-16 10:29:20 +02:00
c4ec2ba88f Added DeserializationError::f_str() (issue #846) 2020-09-14 18:40:00 +02:00
c907ca6e5d Added DeserializationError::EmptyInput 2020-09-13 10:27:29 +02:00
8993a093e9 Travis: run sanitizer on recent GCC version to avoid false positives 2020-09-13 10:25:34 +02:00
d04669d0cc Fixed result of JsonVariant::set((char*)0) (fixes #1368) 2020-09-05 17:33:47 +02:00
05fc136915 Disabled memory sanitizer with clang 6 2020-09-05 13:44:40 +02:00
8d37939086 Added JsonDocument::overflowed() (closes #1358) 2020-09-05 13:44:40 +02:00
6d2ad4539f Simplified the implementation of parseNumber() 2020-09-04 09:23:40 +02:00
5ab53f42b2 Added a return value to visitors 2020-08-29 18:40:27 +02:00
f448e805e9 Removed dead code 2020-08-25 14:36:14 +02:00
67aa8efd5a Added a build failure when nullptr is defined as a macro (closes #1355) 2020-08-23 09:31:40 +02:00
61a5273aea Set version to 6.16.1 2020-08-04 10:33:38 +02:00
35a39b8d8f Fixed deserializeJson() that stopped reading after {} (fixes #1335) 2020-08-04 09:52:42 +02:00
96b6571352 Fixed use-of-uninitialized-value in encodeCodepoint() 2020-08-03 13:44:45 +02:00
74e7dd053f Added memory sanitizer to fuzzers 2020-08-03 13:44:45 +02:00
1f8636d762 Fixed build-arduino-package.sh to ignore .vs/ 2020-08-01 15:35:04 +02:00
e4cfa701d8 Fixed publish.sh to commit CMakeLists.txt 2020-08-01 15:34:27 +02:00
b85181a6db Set version to 6.16.0 2020-08-01 15:17:39 +02:00
6841b80466 Added JsonString::operator!= 2020-07-31 14:02:20 +02:00
298864bafe Set ARDUINOJSON_DECODE_UNICODE to 1 by default 2020-07-30 09:49:09 +02:00
4d7f03836c Added a test that removes an element during iteration (#1332) 2020-07-30 09:20:31 +02:00
c63eb80b95 Shortened the name of the private namespace using hexadecimal digits 2020-07-30 09:18:08 +02:00
0c0bf80074 Suppress IAR compiler warning. 2020-07-28 10:12:41 +02:00
144ff3b06e Updated the change log. Closes #1210 and closes #1314. 2020-07-26 16:09:03 +02:00
d6c50c3596 Reduced stack usage when compiled with -Og (issue #1210)
This saves 128 bytes on ESP8266
2020-07-26 14:51:58 +02:00
51b177ce47 Reduced stack usage when compiled with -Og (issue #1210)
This saves 112 bytes on ESP8266
2020-07-26 12:58:12 +02:00
824b7a25ca Reduced stack usage when compiled with -Og (issue #1210)
This saves 96 bytes on ESP8266
2020-07-26 12:25:13 +02:00
2223d40640 Reduced stack usage when compiler aggressively inlines deserializeJson()
This saves 64 bytes on the recursive part.
2020-07-26 12:25:13 +02:00
4df29fbac1 Fixed "linked" strings incorrectly marked as "owned" (fixes #1318) 2020-07-24 22:25:56 +02:00
6dc36125c2 Added ElementProxy::getOrAddElement() (fixes #1311) 2020-07-23 10:58:47 +02:00
40085609e2 Fixed copyArray() not working with MemberProxy and ElementProxy 2020-07-22 21:08:18 +02:00
ebb6d80092 Fixed copyArray() not working with String 2020-07-22 20:02:33 +02:00
764ff2cd53 Added string deduplication (closes #1303) 2020-07-21 20:15:31 +02:00
8ef226bcb8 Travis: Added Clang 9. Removed Xcode 7.3 2020-07-15 15:27:39 +02:00
63606c0985 Added json_fuzzer and msgpack_fuzzer in test suite 2020-07-15 12:50:00 +02:00
1600d39693 Refactored StringBuilder into StringStorage 2020-07-11 17:51:45 +02:00
04c59985a1 Inverted dependency between MemoryPool and string adapters.
Inserted a null after each raw string in the pool.
2020-07-08 09:38:27 +02:00
7e58347fbe Added comparisons between JsonVariants 2020-07-02 15:28:34 +02:00
0e794a28a1 Simplified implementation of comparison operators 2020-06-13 15:42:04 +02:00
6fb52c3638 Set version to 6.15.2 2020-05-15 09:26:25 +02:00
b72ef09451 Fixed publish script 2020-05-15 09:26:06 +02:00
f7de027617 Break build if using 64-bit integers with ARDUINOJSON_USE_LONG_LONG==0 2020-05-14 10:25:10 +02:00
bc4c2dde33 Moved failing builds to a dedicated folder 2020-05-12 18:11:55 +02:00
95f05dad66 Added JsonVariant as an input type for deserializeXxx() 2020-05-05 13:16:32 +02:00
3bb0a7aa8e Added support for enums in as<T>() and is<T>() (fixes #1256) 2020-05-04 11:13:06 +02:00
dcf7eeef28 CMake: used link_libraries() instead of target_link_libraries() 2020-05-03 10:25:38 +02:00
3b923b6e4e Added support for JsonDocument to copyArray() (issue #1255) 2020-05-02 10:00:08 +02:00
8050f7404b CMake: gathered all build flags in a dedicated file 2020-04-28 16:02:59 +02:00
959b1d9e4c Fixed error C2766 with flag /Zc:__cplusplus on VS2017+ (fixes #1250) 2020-04-28 16:02:38 +02:00
daa279d57b CMake: made project arch-independent 2020-04-11 18:49:28 +02:00
ae9b7926a2 CMake: don't build tests when imported in another project 2020-04-11 18:49:28 +02:00
1f7a5e6943 Fixed links in README 2020-04-11 11:53:08 +02:00
9e354803de Changed publish.sh to update the CMake project version 2020-04-09 21:28:48 +02:00
170 changed files with 5620 additions and 2698 deletions

View File

@ -21,17 +21,17 @@ matrix:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.8']
env: SCRIPT=test _CC=gcc-4.8 _CXX=g++-4.8 SANITIZE=address
env: SCRIPT=test _CC=gcc-4.8 _CXX=g++-4.8
- addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-4.9']
env: SCRIPT=test _CC=gcc-4.9 _CXX=g++-4.9 SANITIZE=leak
env: SCRIPT=test _CC=gcc-4.9 _CXX=g++-4.9
- addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-5']
env: SCRIPT=test _CC=gcc-5 _CXX=g++-5 # SANITIZE=undefined
env: SCRIPT=test _CC=gcc-5 _CXX=g++-5
- addons:
apt:
sources: ['ubuntu-toolchain-r-test']
@ -41,17 +41,17 @@ matrix:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-7']
env: SCRIPT=test _CC=gcc-7 _CXX=g++-7
env: SCRIPT=test _CC=gcc-7 _CXX=g++-7 CXXFLAGS="-fsanitize=leak"
- addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-8']
env: SCRIPT=test _CC=gcc-8 _CXX=g++-8
env: SCRIPT=test _CC=gcc-8 _CXX=g++-8 CXXFLAGS="-fsanitize=undefined" LDFLAGS="-fuse-ld=gold"
- addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['g++-9']
env: SCRIPT=test _CC=gcc-9 _CXX=g++-9
env: SCRIPT=test _CC=gcc-9 _CXX=g++-9 CXXFLAGS="-fsanitize=address"
- addons:
apt:
packages: ['g++-arm-linux-gnueabihf']
@ -61,12 +61,12 @@ matrix:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5']
packages: ['clang-3.5']
env: SCRIPT=test _CC=clang-3.5 _CXX=clang++-3.5 SANITIZE=address
env: SCRIPT=test _CC=clang-3.5 _CXX=clang++-3.5 CXXFLAGS="-fsanitize=address"
- addons:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.6']
packages: ['clang-3.6']
env: SCRIPT=test _CC=clang-3.6 _CXX=clang++-3.6 SANITIZE=leak
env: SCRIPT=test _CC=clang-3.6 _CXX=clang++-3.6 CXXFLAGS="-fsanitize=leak"
- addons:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.7']
@ -76,7 +76,7 @@ matrix:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-precise-3.8']
packages: ['clang-3.8']
env: SCRIPT=test _CC=clang-3.8 _CXX=clang++-3.8 SANITIZE=undefined
env: SCRIPT=test _CC=clang-3.8 _CXX=clang++-3.8 CXXFLAGS="-fsanitize=undefined"
- addons:
apt:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-3.9']
@ -107,10 +107,14 @@ matrix:
sources: ['ubuntu-toolchain-r-test','llvm-toolchain-trusty-8']
packages: ['clang-8']
env: SCRIPT=test _CC=clang-8 _CXX=clang++-8
- addons:
apt:
sources:
- sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
packages: ['clang-9']
env: SCRIPT=test _CC=clang-9 _CXX=clang++-9
- env: SCRIPT=coverage
- os: osx
osx_image: xcode7.3
env: SCRIPT=test
- os: osx
osx_image: xcode8.3
env: SCRIPT=test
@ -119,16 +123,11 @@ matrix:
env: SCRIPT=test
- os: osx
osx_image: xcode10
env: SCRIPT=test SANITIZE=address
env: SCRIPT=test CXXFLAGS="-fsanitize=address"
- env: SCRIPT=arduino VERSION=1.6.7 BOARD=arduino:avr:uno
- env: SCRIPT=arduino VERSION=1.8.2 BOARD=arduino:samd:mkr1000
- env: SCRIPT=platformio BOARD=uno
- env: SCRIPT=platformio BOARD=esp01
- addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages: ['clang-6.0','llvm-6.0']
env: SCRIPT=fuzz CLANG=6.0
cache:
directories:
- "~/.platformio"

View File

@ -1,6 +1,49 @@
ArduinoJson: change log
=======================
v6.17.0 (2020-10-19)
-------
* Added a build failure when nullptr is defined as a macro (issue #1355)
* Added `JsonDocument::overflowed()` which tells if the memory pool was too small (issue #1358)
* Added `DeserializationError::EmptyInput` which tells if the input was empty
* Added `DeserializationError::f_str()` which returns a `const __FlashStringHelper*` (issue #846)
* Added `operator|(JsonVariantConst, JsonVariantConst)`
* Added filtering for MessagePack (issue #1298, PR #1394 by Luca Passarella)
* Moved float convertion tables to PROGMEM
* Fixed `JsonVariant::set((char*)0)` which returned false instead of true (issue #1368)
* Fixed error `No such file or directory #include <WString.h>` (issue #1381)
v6.16.1 (2020-08-04)
-------
* Fixed `deserializeJson()` that stopped reading after `{}` (issue #1335)
v6.16.0 (2020-08-01)
-------
* Added comparisons (`>`, `>=`, `==`, `!=`, `<`, and `<=`) between `JsonVariant`s
* Added string deduplication (issue #1303)
* Added `JsonString::operator!=`
* Added wildcard key (`*`) for filters (issue #1309)
* Set `ARDUINOJSON_DECODE_UNICODE` to `1` by default
* Fixed `copyArray()` not working with `String`, `ElementProxy`, and `MemberProxy`
* Fixed error `getOrAddElement is not a member of ElementProxy` (issue #1311)
* Fixed excessive stack usage when compiled with `-Og` (issues #1210 and #1314)
* Fixed `Warning[Pa093]: implicit conversion from floating point to integer` on IAR compiler (PR #1328 by @stawiski)
v6.15.2 (2020-05-15)
-------
* CMake: don't build tests when imported in another project
* CMake: made project arch-independent
* Visual Studio: fixed error C2766 with flag `/Zc:__cplusplus` (issue #1250)
* Added support for `JsonDocument` to `copyArray()` (issue #1255)
* Added support for `enum`s in `as<T>()` and `is<T>()` (issue #1256)
* Added `JsonVariant` as an input type for `deserializeXxx()`
For example, you can do: `deserializeJson(doc2, doc1["payload"])`
* Break the build if using 64-bit integers with ARDUINOJSON_USE_LONG_LONG==0
v6.15.1 (2020-04-08)
-------

View File

@ -3,42 +3,17 @@
# MIT License
cmake_minimum_required(VERSION 3.0)
project(ArduinoJson)
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)
project(ArduinoJson VERSION 6.17.0)
enable_testing()
add_definitions(-DARDUINOJSON_DEBUG=1)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8)
add_compile_options(-g -Og)
else()
add_compile_options(-g -O0)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0)
add_compile_options(-g -Og)
else()
add_compile_options(-g -O0)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0)
add_compile_options(-g -Og)
else()
add_compile_options(-g -O0)
endif()
endif()
if(${COVERAGE})
set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage")
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)
endif()
add_subdirectory(src)
add_subdirectory(extras/tests)
add_subdirectory(extras/fuzzing)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
include(extras/CompileOptions.cmake)
add_subdirectory(extras/tests)
add_subdirectory(extras/fuzzing)
endif()

View File

@ -2,7 +2,7 @@
---
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.15.1)](https://www.ardu-badge.com/ArduinoJson/6.15.1)
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.17.0)](https://www.ardu-badge.com/ArduinoJson/6.17.0)
[![Build Status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
[![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=6.x)](https://travis-ci.org/bblanchon/ArduinoJson)
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
@ -17,7 +17,7 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
* [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v6/api/config/decode_unicode/?utm_source=github&utm_medium=readme)
* [Optionally stores links to the input buffer (zero-copy)](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme)
* [Optionally supports comments in the input](https://arduinojson.org/v6/api/config/enable_comments/?utm_source=github&utm_medium=readme)
* [Optionally filters the input to keep only desired values](https://arduinojson.org/v6/api/json/deserializejson/#filtering?utm_source=github&utm_medium=readme)
* [Optionally filters the input to keep only desired values](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#filtering)
* Supports single quotes as a string delimiter
* Compatible with NDJSON and JSON Lines
* [JSON serialization](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme)
@ -31,12 +31,13 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
* [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme)
* [Fixed memory allocation, no heap fragmentation](https://arduinojson.org/v6/api/jsondocument/?utm_source=github&utm_medium=readme)
* [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/?utm_source=github&utm_medium=readme)
* Deduplicates strings
* Versatile
* [Supports custom allocators (to use external RAM chip, for example)](https://arduinojson.org/v6/how-to/use-external-ram-on-esp32/?utm_source=github&utm_medium=readme)
* Supports [Arduino's `String`](https://arduinojson.org/v6/api/config/enable_arduino_string/) and [STL's `std::string`](https://arduinojson.org/v6/api/config/enable_std_string/?utm_source=github&utm_medium=readme)
* Supports Arduino's `Stream` and [STL's `std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/?utm_source=github&utm_medium=readme)
* [Supports Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/?utm_source=github&utm_medium=readme)
* Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/#custom-writer?utm_source=github&utm_medium=readme)
* Supports [custom readers](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme#custom-reader) and [custom writers](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme#custom-writer)
* Portable
* Usable on any C++ project (not limited to Arduino)
* Compatible with C++98
@ -62,14 +63,15 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
* [Visual Micro](http://www.visualmicro.com/)
* [Visual Studio](https://www.visualstudio.com/)
* [Even works with online compilers like wandbox.org](https://wandbox.org/permlink/t7KP7I6dVuLhqzDl)
* [CMake friendly](https://arduinojson.org/v6/how-to/use-arduinojson-with-cmake/?utm_source=github&utm_medium=readme)
* Well designed
* [Elegant API](http://127.0.0.1:4000/v6/example/)
* [Elegant API](http://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme)
* [Thread-safe](https://en.wikipedia.org/wiki/Thread_safety)
* Self-contained (no external dependency)
* `const` friendly
* [`for` friendly](https://arduinojson.org/v6/api/jsonobject/begin_end/?utm_source=github&utm_medium=readme)
* [TMP friendly](https://en.wikipedia.org/wiki/Template_metaprogramming)
* Handles [integer overflows](https://arduinojson.org/v6/api/jsonvariant/as/#integer-overflows?utm_source=github&utm_medium=readme)
* Handles [integer overflows](https://arduinojson.org/v6/api/jsonvariant/as/?utm_source=github&utm_medium=readme#integer-overflows)
* Well tested
* [Unit test coverage close to 100%](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x)
* Continuously tested on

View File

@ -1,4 +1,4 @@
version: 6.15.1.{build}
version: 6.17.0.{build}
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019

View File

@ -82,7 +82,7 @@ void setup() {
DeserializationError error = deserializeJson(doc, client);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
Serial.println(error.f_str());
return;
}

View File

@ -42,7 +42,7 @@ void setup() {
// Test if parsing succeeds.
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
Serial.println(error.f_str());
return;
}

View File

@ -50,7 +50,7 @@ void setup() {
// Test if parsing succeeded.
if (error) {
Serial.print("deserializeMsgPack() failed: ");
Serial.println(error.c_str());
Serial.println(error.f_str());
return;
}

100
extras/CompileOptions.cmake Normal file
View File

@ -0,0 +1,100 @@
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
add_compile_options(
-pedantic
-Wall
-Wcast-align
-Wcast-qual
-Wconversion
-Wctor-dtor-privacy
-Wdisabled-optimization
-Werror
-Wextra
-Wformat=2
-Winit-self
-Wmissing-include-dirs
-Wnon-virtual-dtor
-Wold-style-cast
-Woverloaded-virtual
-Wparentheses
-Wredundant-decls
-Wshadow
-Wsign-promo
-Wstrict-aliasing
-Wundef
)
if(NOT MINGW)
add_compile_options(
-std=c++98
)
endif()
if(${COVERAGE})
set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage")
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8) AND (NOT ${COVERAGE}))
add_compile_options(-g -Og)
else()
add_compile_options(-g -O0)
endif()
add_compile_options(
-Wstrict-null-sentinel
-Wno-vla # Allow VLA in tests
)
add_definitions(-DHAS_VARIABLE_LENGTH_ARRAY)
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.5)
add_compile_options(-Wlogical-op) # the flag exists in 4.4 but is buggy
endif()
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6)
add_compile_options(-Wnoexcept)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(
-Wc++11-compat
-Wdeprecated-register
-Wno-vla-extension # Allow VLA in tests
)
add_definitions(
-DHAS_VARIABLE_LENGTH_ARRAY
-DSUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR
)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0) AND (NOT ${COVERAGE}))
add_compile_options(-g -Og)
else()
add_compile_options(-g -O0)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0) AND (NOT ${COVERAGE}))
add_compile_options(-g -Og)
else()
add_compile_options(-g -O0)
endif()
endif()
if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_compile_options(
/W4 # Set warning level
/WX # Treats all compiler warnings as errors.
)
if (NOT MSVC_VERSION LESS 1910) # >= Visual Studio 2017
add_compile_options(
/Zc:__cplusplus # Enable updated __cplusplus macro
)
endif()
endif()

View File

@ -3,12 +3,5 @@
export CC="$_CC"
export CXX="$_CXX"
if [ -n "$SANITIZE" ]; then
export CXXFLAGS="-fsanitize=$SANITIZE"
BUILD_TYPE="Debug"
else
BUILD_TYPE="Release"
fi
cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE .
cmake -DCMAKE_BUILD_TYPE=Release .
cmake --build .

View File

@ -1,26 +1,22 @@
#!/bin/bash -eux
ROOT_DIR=$(dirname $0)/../../
INCLUDE_DIR=${ROOT_DIR}/src/
FUZZING_DIR=${ROOT_DIR}/extras/fuzzing/
CXXFLAGS="-g -fprofile-instr-generate -fcoverage-mapping -fsanitize=address,undefined,fuzzer -fno-sanitize-recover=all"
fuzz() {
NAME="$1"
FUZZER="${NAME}_fuzzer"
FUZZER_CPP="${FUZZING_DIR}/${NAME}_fuzzer.cpp"
CORPUS_DIR="${FUZZING_DIR}/${NAME}_corpus"
SEED_CORPUS_DIR="${FUZZING_DIR}/${NAME}_seed_corpus"
export CC="clang-${CLANG}"
export CXX="clang++-${CLANG}"
cmake -DCMAKE_BUILD_TYPE=Debug .
clang++-${CLANG} ${CXXFLAGS} -o ${FUZZER} -I$INCLUDE_DIR ${FUZZER_CPP}
FUZZER_TARGET="${FUZZER}_fuzzer"
FUZZER_PATH="extras/fuzzing/${FUZZER_TARGET}"
CORPUS_DIR="${FUZZING_DIR}/${FUZZER}_corpus"
SEED_CORPUS_DIR="${FUZZING_DIR}/${FUZZER}_seed_corpus"
export ASAN_OPTIONS="detect_leaks=0"
export LLVM_PROFILE_FILE="${FUZZER}.profraw"
./${FUZZER} "$CORPUS_DIR" "$SEED_CORPUS_DIR" -max_total_time=30 -timeout=1
cmake --build . --target $FUZZER_TARGET
llvm-profdata-${CLANG} merge -sparse ${LLVM_PROFILE_FILE} -o ${FUZZER}.profdata
llvm-cov-${CLANG} report ./${FUZZER} -instr-profile=${FUZZER}.profdata
}
export ASAN_OPTIONS="detect_leaks=0"
export LLVM_PROFILE_FILE="${FUZZER_TARGET}.profraw"
${FUZZER_PATH} "$CORPUS_DIR" "$SEED_CORPUS_DIR" -max_total_time=60 -timeout=1
fuzz json
fuzz msgpack
llvm-profdata-${CLANG} merge -sparse ${LLVM_PROFILE_FILE} -o ${FUZZER_TARGET}.profdata
llvm-cov-${CLANG} report ./${FUZZER_PATH} -instr-profile=${FUZZER_TARGET}.profdata

View File

@ -1,4 +1,8 @@
#!/bin/sh -ex
"$(dirname "$0")/build.sh"
export CC="$_CC"
export CXX="$_CXX"
cmake -DCMAKE_BUILD_TYPE=Debug .
cmake --build .
ctest --output-on-failure .

View File

@ -6,18 +6,72 @@ if(MSVC)
add_compile_options(-D_CRT_SECURE_NO_WARNINGS)
endif()
add_executable(msgpack_fuzzer
add_executable(msgpack_reproducer
msgpack_fuzzer.cpp
fuzzer_main.cpp
reproducer.cpp
)
target_link_libraries(msgpack_fuzzer
target_link_libraries(msgpack_reproducer
ArduinoJson
)
add_executable(json_fuzzer
add_executable(json_reproducer
json_fuzzer.cpp
fuzzer_main.cpp
reproducer.cpp
)
target_link_libraries(json_fuzzer
target_link_libraries(json_reproducer
ArduinoJson
)
# Infer path of llvm-symbolizer from the path of clang
string(REPLACE "clang++" "llvm-symbolizer" LLVM_SYMBOLIZER ${CMAKE_CXX_COMPILER})
macro(add_fuzzer name mode)
set(FUZZER "${name}_${mode}_fuzzer")
set(CORPUS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${name}_corpus")
set(SEED_CORPUS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${name}_seed_corpus")
add_executable("${FUZZER}"
"${name}_fuzzer.cpp"
)
target_link_libraries("${FUZZER}"
ArduinoJson
)
set_target_properties("${FUZZER}"
PROPERTIES
COMPILE_FLAGS
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=${mode},fuzzer -fno-sanitize-recover=all"
LINK_FLAGS
"-fprofile-instr-generate -fcoverage-mapping -fsanitize=${mode},fuzzer -fno-sanitize-recover=all"
)
add_test(
NAME
"${FUZZER}"
COMMAND
"${FUZZER}" "${CORPUS_DIR}" "${SEED_CORPUS_DIR}" -max_total_time=5 -timeout=1
)
set_tests_properties("${FUZZER}"
PROPERTIES
ENVIRONMENT
ASAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
ENVIRONMENT
LLVM_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
ENVIRONMENT
MSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
ENVIRONMENT
UBSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER}
)
endmacro()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6)
add_fuzzer(json address)
add_fuzzer(json undefined)
add_fuzzer(msgpack address)
add_fuzzer(msgpack undefined)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7)
# We're getting false positive with Clang 6
add_fuzzer(json memory)
add_fuzzer(msgpack memory)
endif()

View File

@ -21,7 +21,7 @@ std::vector<uint8_t> read(const char* path) {
}
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
size_t size = static_cast<size_t>(ftell(f));
fseek(f, 0, SEEK_SET);
std::vector<uint8_t> buffer(size);

View File

@ -10,6 +10,7 @@ rm -f $OUTPUT
# create zip
7z a $OUTPUT \
-xr!.vs \
ArduinoJson/CHANGELOG.md \
ArduinoJson/examples \
ArduinoJson/src \

View File

@ -1,29 +0,0 @@
#!/bin/bash
export PATH="$PATH:/Applications/CMake.app/Contents/bin/"
cd $(dirname $0)/../..
ROOT=$(pwd)
mkdir "build"
cd build
BUILD=$(pwd)
build-env()
{
cd $BUILD
mkdir "$1"
cd "$1"
cmake "$ROOT" -G "$2"
}
if [[ $(uname) == MINGW* ]]
then
build-env "Make" "MinGW Makefiles"
build-env "SublimeText" "Sublime Text 2 - Ninja"
build-env "VisualStudio" "Visual Studio 14 2015"
else
build-env "SublimeText" "Sublime Text 2 - Ninja"
build-env "Make" "Unix Makefiles"
build-env "Xcode" "Xcode"
fi

View File

@ -14,19 +14,22 @@ update_version_in_source () {
UNDERLINE=$(printf -- '-%.0s' $(seq 1 ${#TAG}))
sed -i~ -bE "s/version=$VERSION_REGEX/version=$VERSION/; s|ardu-badge.com/ArduinoJson/$VERSION_REGEX|ardu-badge.com/ArduinoJson/$VERSION|; " README.md
rm README.md*~
rm README.md~
sed -i~ -bE "4s/HEAD/$TAG ($DATE)/; 5s/-+/$UNDERLINE/" CHANGELOG.md
rm CHANGELOG.md*~
rm CHANGELOG.md~
sed -i~ -bE "s/(project\\s*\\(ArduinoJson\\s+VERSION\\s+).*?\\)/\\1$MAJOR.$MINOR.$REVISION)/" CMakeLists.txt
rm CMakeLists.txt~
sed -i~ -bE "s/\"version\":.*$/\"version\": \"$VERSION\",/" library.json
rm library.json*~
rm library.json~
sed -i~ -bE "s/version=.*$/version=$VERSION/" library.properties
rm library.properties*~
rm library.properties~
sed -i~ -bE "s/version: .*$/version: $VERSION.{build}/" appveyor.yml
rm appveyor.yml*~
rm appveyor.yml~
sed -i~ -bE \
-e "s/ARDUINOJSON_VERSION .*$/ARDUINOJSON_VERSION \"$VERSION\"/" \
@ -38,7 +41,7 @@ update_version_in_source () {
}
commit_new_version () {
git add src/ArduinoJson/version.hpp README.md CHANGELOG.md library.json library.properties appveyor.yml
git add src/ArduinoJson/version.hpp README.md CHANGELOG.md library.json library.properties appveyor.yml CMakeLists.txt
git commit -m "Set version to $VERSION"
}

View File

@ -4,76 +4,11 @@
add_subdirectory(catch)
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
add_compile_options(
-pedantic
-Wall
-Wcast-align
-Wcast-qual
-Wconversion
-Wctor-dtor-privacy
-Wdisabled-optimization
-Werror
-Wextra
-Wformat=2
-Winit-self
-Wmissing-include-dirs
-Wnon-virtual-dtor
-Wold-style-cast
-Woverloaded-virtual
-Wparentheses
-Wredundant-decls
-Wshadow
-Wsign-promo
-Wstrict-aliasing
-Wundef
)
if(NOT MINGW)
add_compile_options(
-std=c++98
)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
add_compile_options(
-Wstrict-null-sentinel
-Wno-vla # Allow VLA in tests
)
add_definitions(-DHAS_VARIABLE_LENGTH_ARRAY)
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.5)
add_compile_options(-Wlogical-op) # the flag exists in 4.4 but is buggy
endif()
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6)
add_compile_options(-Wnoexcept)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(
-Wc++11-compat
-Wdeprecated-register
-Wno-vla-extension # Allow VLA in tests
)
add_definitions(
-DHAS_VARIABLE_LENGTH_ARRAY
-DSUBSCRIPT_CONFLICTS_WITH_BUILTIN_OPERATOR
)
endif()
if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_compile_options(
/W4 # Set warning level
/WX # Treats all compiler warnings as errors.
)
endif()
link_libraries(ArduinoJson catch)
include_directories(Helpers)
add_subdirectory(ElementProxy)
add_subdirectory(FailingBuilds)
add_subdirectory(IntegrationTests)
add_subdirectory(JsonArray)
add_subdirectory(JsonDeserializer)

View File

@ -9,11 +9,7 @@ add_executable(ElementProxyTests
remove.cpp
set.cpp
size.cpp
)
target_link_libraries(ElementProxyTests
ArduinoJson
catch
subscript.cpp
)
add_test(ElementProxy ElementProxyTests)

View File

@ -10,19 +10,39 @@ using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("ElementProxy::operator==()") {
DynamicJsonDocument doc(4096);
SECTION("same value") {
SECTION("1 vs 1") {
doc.add(1);
doc.add(1);
REQUIRE(doc[0] <= doc[1]);
REQUIRE(doc[0] == doc[1]);
REQUIRE(doc[0] >= doc[1]);
REQUIRE_FALSE(doc[0] != doc[1]);
REQUIRE_FALSE(doc[0] < doc[1]);
REQUIRE_FALSE(doc[0] > doc[1]);
}
SECTION("different values") {
SECTION("1 vs 2") {
doc.add(1);
doc.add(2);
REQUIRE_FALSE(doc[0] == doc[1]);
REQUIRE(doc[0] != doc[1]);
REQUIRE(doc[0] < doc[1]);
REQUIRE(doc[0] <= doc[1]);
REQUIRE_FALSE(doc[0] == doc[1]);
REQUIRE_FALSE(doc[0] > doc[1]);
REQUIRE_FALSE(doc[0] >= doc[1]);
}
SECTION("'abc' vs 'bcd'") {
doc.add("abc");
doc.add("bcd");
REQUIRE(doc[0] != doc[1]);
REQUIRE(doc[0] < doc[1]);
REQUIRE(doc[0] <= doc[1]);
REQUIRE_FALSE(doc[0] == doc[1]);
REQUIRE_FALSE(doc[0] > doc[1]);
REQUIRE_FALSE(doc[0] >= doc[1]);
}
}

View File

@ -7,7 +7,7 @@
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemberProxy::operator[]") {
TEST_CASE("ElementProxy::operator[]") {
DynamicJsonDocument doc(4096);
ElementProxy<JsonDocument&> ep = doc[1];

View File

@ -0,0 +1,38 @@
# ArduinoJson - arduinojson.org
# Copyright Benoit Blanchon 2014-2020
# MIT License
macro(build_should_fail target)
set_target_properties(${target}
PROPERTIES
EXCLUDE_FROM_ALL TRUE
EXCLUDE_FROM_DEFAULT_BUILD TRUE
)
add_test(
NAME
${target}
COMMAND
${CMAKE_COMMAND} --build . --target ${target} --config $<CONFIGURATION>
WORKING_DIRECTORY
${CMAKE_BINARY_DIR}
)
set_tests_properties(${target}
PROPERTIES
WILL_FAIL TRUE
)
endmacro()
add_executable(Issue978 Issue978.cpp)
build_should_fail(Issue978)
add_executable(Issue1189 Issue1189.cpp)
build_should_fail(Issue1189)
add_executable(read_long_long read_long_long.cpp)
set_property(TARGET read_long_long PROPERTY CXX_STANDARD 11)
build_should_fail(read_long_long)
add_executable(write_long_long write_long_long.cpp)
set_property(TARGET write_long_long PROPERTY CXX_STANDARD 11)
build_should_fail(write_long_long)

View File

@ -0,0 +1,20 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#define ARDUINOJSON_USE_LONG_LONG 0
#include <ArduinoJson.h>
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
#error This test requires sizeof(long) < 8
#endif
#if !ARDUINOJSON_HAS_LONG_LONG
#error This test requires C++11
#endif
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(long long)
int main() {
DynamicJsonDocument doc(1024);
doc["dummy"].as<long long>();
}

View File

@ -0,0 +1,19 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#define ARDUINOJSON_USE_LONG_LONG 0
#include <ArduinoJson.h>
#if defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ >= 8
#error This test requires sizeof(long) < 8
#endif
#if !ARDUINOJSON_HAS_LONG_LONG
#error This test requires C++11
#endif
int main() {
DynamicJsonDocument doc(1024);
doc["dummy"] = static_cast<long long>(42);
}

View File

@ -0,0 +1,8 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#pragma once
#include "api/Stream.h"
#include "api/String.h"

View File

@ -9,6 +9,9 @@
// Reproduces Arduino's String class
class String {
public:
String() {}
explicit String(const char* s) : _str(s) {}
String& operator+=(const char* rhs) {
_str += rhs;
return *this;

View File

@ -5,6 +5,8 @@
#include <stdint.h> // uint8_t
#include <string.h> // strcmp, strlen...
#define PROGMEM
class __FlashStringHelper;
inline const void* convertPtrToFlash(const void* s) {
@ -15,9 +17,26 @@ inline const void* convertFlashToPtr(const void* s) {
return reinterpret_cast<const char*>(s) - 42;
}
#define F(X) reinterpret_cast<const __FlashStringHelper*>(convertPtrToFlash(X))
#define FC(X) reinterpret_cast<const char*>(convertPtrToFlash(X))
#define PSTR(X) reinterpret_cast<const char*>(convertPtrToFlash(X))
#define F(X) reinterpret_cast<const __FlashStringHelper*>(PSTR(X))
inline uint8_t pgm_read_byte(const void* p) {
return *reinterpret_cast<const uint8_t*>(convertFlashToPtr(p));
}
inline void* pgm_read_ptr(const void* p) {
return *reinterpret_cast<void* const*>(convertFlashToPtr(p));
}
inline float pgm_read_float(const void* p) {
return *reinterpret_cast<const float*>(convertFlashToPtr(p));
}
inline uint32_t pgm_read_dword(const void* p) {
return *reinterpret_cast<const uint32_t*>(convertFlashToPtr(p));
}
#define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \
static type const ARDUINOJSON_CONCAT2(name, _progmem)[] = value; \
static type const* name = reinterpret_cast<type const*>( \
convertPtrToFlash(ARDUINOJSON_CONCAT2(name, _progmem)));

View File

@ -16,9 +16,4 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
)
endif()
target_link_libraries(IntegrationTests
ArduinoJson
catch
)
add_test(IntegrationTests IntegrationTests)

View File

@ -19,9 +19,4 @@ add_executable(JsonArrayTests
undefined.cpp
)
target_link_libraries(JsonArrayTests
ArduinoJson
catch
)
add_test(JsonArray JsonArrayTests)

View File

@ -102,13 +102,13 @@ TEST_CASE("JsonArray::add()") {
SECTION("should duplicate char*") {
array.add(const_cast<char*>("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string") {
array.add(std::string("world"));
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}

View File

@ -6,20 +6,57 @@
#include <catch.hpp>
TEST_CASE("copyArray()") {
SECTION("1D -> JsonArray") {
SECTION("int[] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
char json[32];
int source[] = {1, 2, 3};
bool ok = copyArray(source, array);
REQUIRE(ok);
CHECK(ok);
serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[1,2,3]") == json);
serializeJson(array, json);
CHECK(std::string("[1,2,3]") == json);
}
SECTION("1D -> JsonArray, but not enough memory") {
SECTION("std::string[] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
char json[32];
std::string source[] = {"a", "b", "c"};
bool ok = copyArray(source, array);
CHECK(ok);
serializeJson(array, json);
CHECK(std::string("[\"a\",\"b\",\"c\"]") == json);
}
SECTION("int[] -> JsonDocument") {
DynamicJsonDocument doc(4096);
char json[32];
int source[] = {1, 2, 3};
bool ok = copyArray(source, doc);
CHECK(ok);
serializeJson(doc, json);
CHECK(std::string("[1,2,3]") == json);
}
SECTION("int[] -> MemberProxy") {
DynamicJsonDocument doc(4096);
char json[32];
int source[] = {1, 2, 3};
bool ok = copyArray(source, doc["data"]);
CHECK(ok);
serializeJson(doc, json);
CHECK(std::string("{\"data\":[1,2,3]}") == json);
}
SECTION("int[] -> JsonArray, but not enough memory") {
const size_t SIZE = JSON_ARRAY_SIZE(2);
StaticJsonDocument<SIZE> doc;
JsonArray array = doc.to<JsonArray>();
@ -29,24 +66,48 @@ TEST_CASE("copyArray()") {
bool ok = copyArray(source, array);
REQUIRE_FALSE(ok);
serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[1,2]") == json);
serializeJson(array, json);
CHECK(std::string("[1,2]") == json);
}
SECTION("2D -> JsonArray") {
SECTION("int[][] -> JsonArray") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
char json[32];
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
bool ok = copyArray(source, array);
REQUIRE(ok);
CHECK(ok);
serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[[1,2,3],[4,5,6]]") == json);
serializeJson(array, json);
CHECK(std::string("[[1,2,3],[4,5,6]]") == json);
}
SECTION("2D -> JsonArray, but not enough memory") {
SECTION("int[][] -> MemberProxy") {
DynamicJsonDocument doc(4096);
char json[32];
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
bool ok = copyArray(source, doc["data"]);
CHECK(ok);
serializeJson(doc, json);
CHECK(std::string("{\"data\":[[1,2,3],[4,5,6]]}") == json);
}
SECTION("int[][] -> JsonDocument") {
DynamicJsonDocument doc(4096);
char json[32];
int source[][3] = {{1, 2, 3}, {4, 5, 6}};
bool ok = copyArray(source, doc);
CHECK(ok);
serializeJson(doc, json);
CHECK(std::string("[[1,2,3],[4,5,6]]") == json);
}
SECTION("int[][] -> JsonArray, but not enough memory") {
const size_t SIZE =
JSON_ARRAY_SIZE(2) + JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(2);
StaticJsonDocument<SIZE> doc;
@ -60,58 +121,159 @@ TEST_CASE("copyArray()") {
CAPTURE(doc.memoryUsage());
CHECK_FALSE(ok);
serializeJson(array, json, sizeof(json));
REQUIRE(std::string("[[1,2,3],[4,5]]") == json);
serializeJson(array, json);
CHECK(std::string("[[1,2,3],[4,5]]") == json);
}
SECTION("JsonArray -> 1D, with more space than needed") {
SECTION("JsonArray -> int[], with more space than needed") {
DynamicJsonDocument doc(4096);
char json[] = "[1,2,3]";
DeserializationError err = deserializeJson(doc, json);
REQUIRE(err == DeserializationError::Ok);
CHECK(err == DeserializationError::Ok);
JsonArray array = doc.as<JsonArray>();
int destination[4] = {0};
size_t result = copyArray(array, destination);
REQUIRE(3 == result);
REQUIRE(1 == destination[0]);
REQUIRE(2 == destination[1]);
REQUIRE(3 == destination[2]);
REQUIRE(0 == destination[3]);
CHECK(3 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
CHECK(3 == destination[2]);
CHECK(0 == destination[3]);
}
SECTION("JsonArray -> 1D, without enough space") {
SECTION("JsonArray -> int[], without enough space") {
DynamicJsonDocument doc(4096);
char json[] = "[1,2,3]";
DeserializationError err = deserializeJson(doc, json);
REQUIRE(err == DeserializationError::Ok);
CHECK(err == DeserializationError::Ok);
JsonArray array = doc.as<JsonArray>();
int destination[2] = {0};
size_t result = copyArray(array, destination);
REQUIRE(2 == result);
REQUIRE(1 == destination[0]);
REQUIRE(2 == destination[1]);
CHECK(2 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
}
SECTION("JsonArray -> 2D") {
SECTION("JsonArray -> std::string[]") {
DynamicJsonDocument doc(4096);
char json[] = "[\"a\",\"b\",\"c\"]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
JsonArray array = doc.as<JsonArray>();
std::string destination[4];
size_t result = copyArray(array, destination);
CHECK(3 == result);
CHECK("a" == destination[0]);
CHECK("b" == destination[1]);
CHECK("c" == destination[2]);
CHECK("" == destination[3]);
}
SECTION("JsonDocument -> int[]") {
DynamicJsonDocument doc(4096);
char json[] = "[1,2,3]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
int destination[4] = {0};
size_t result = copyArray(doc, destination);
CHECK(3 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
CHECK(3 == destination[2]);
CHECK(0 == destination[3]);
}
SECTION("MemberProxy -> int[]") {
DynamicJsonDocument doc(4096);
char json[] = "{\"data\":[1,2,3]}";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
int destination[4] = {0};
size_t result = copyArray(doc["data"], destination);
CHECK(3 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
CHECK(3 == destination[2]);
CHECK(0 == destination[3]);
}
SECTION("ElementProxy -> int[]") {
DynamicJsonDocument doc(4096);
char json[] = "[[1,2,3]]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
int destination[4] = {0};
size_t result = copyArray(doc[0], destination);
CHECK(3 == result);
CHECK(1 == destination[0]);
CHECK(2 == destination[1]);
CHECK(3 == destination[2]);
CHECK(0 == destination[3]);
}
SECTION("JsonArray -> int[][]") {
DynamicJsonDocument doc(4096);
char json[] = "[[1,2],[3],[4]]";
DeserializationError err = deserializeJson(doc, json);
REQUIRE(err == DeserializationError::Ok);
CHECK(err == DeserializationError::Ok);
JsonArray array = doc.as<JsonArray>();
int destination[3][2] = {{0}};
copyArray(array, destination);
REQUIRE(1 == destination[0][0]);
REQUIRE(2 == destination[0][1]);
REQUIRE(3 == destination[1][0]);
REQUIRE(0 == destination[1][1]);
REQUIRE(4 == destination[2][0]);
REQUIRE(0 == destination[2][1]);
CHECK(1 == destination[0][0]);
CHECK(2 == destination[0][1]);
CHECK(3 == destination[1][0]);
CHECK(0 == destination[1][1]);
CHECK(4 == destination[2][0]);
CHECK(0 == destination[2][1]);
}
SECTION("JsonDocument -> int[][]") {
DynamicJsonDocument doc(4096);
char json[] = "[[1,2],[3],[4]]";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
int destination[3][2] = {{0}};
copyArray(doc, destination);
CHECK(1 == destination[0][0]);
CHECK(2 == destination[0][1]);
CHECK(3 == destination[1][0]);
CHECK(0 == destination[1][1]);
CHECK(4 == destination[2][0]);
CHECK(0 == destination[2][1]);
}
SECTION("MemberProxy -> int[][]") {
DynamicJsonDocument doc(4096);
char json[] = "{\"data\":[[1,2],[3],[4]]}";
DeserializationError err = deserializeJson(doc, json);
CHECK(err == DeserializationError::Ok);
int destination[3][2] = {{0}};
copyArray(doc["data"], destination);
CHECK(1 == destination[0][0]);
CHECK(2 == destination[0][1]);
CHECK(3 == destination[1][0]);
CHECK(0 == destination[1][1]);
CHECK(4 == destination[2][0]);
CHECK(0 == destination[2][1]);
}
}

View File

@ -65,4 +65,15 @@ TEST_CASE("JsonArray::remove()") {
REQUIRE(_array[0] == 1);
REQUIRE(_array[1] == 2);
}
SECTION("In a loop") {
for (JsonArray::iterator it = _array.begin(); it != _array.end(); ++it) {
if (*it == 2)
_array.remove(it);
}
REQUIRE(2 == _array.size());
REQUIRE(_array[0] == 1);
REQUIRE(_array[1] == 3);
}
}

View File

@ -119,13 +119,13 @@ TEST_CASE("JsonArray::operator[]") {
SECTION("should duplicate char*") {
array[0] = const_cast<char*>("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string") {
array[0] = std::string("world");
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}

View File

@ -18,11 +18,6 @@ add_executable(JsonDeserializerTests
string.cpp
)
target_link_libraries(JsonDeserializerTests
ArduinoJson
catch
)
set_target_properties(JsonDeserializerTests PROPERTIES UNITY_BUILD OFF)
add_test(JsonDeserializer JsonDeserializerTests)

View File

@ -30,20 +30,22 @@ void testBoolification(DeserializationError error, bool expected) {
TEST_CASE("DeserializationError") {
SECTION("c_str()") {
TEST_STRINGIFICATION(Ok);
TEST_STRINGIFICATION(TooDeep);
TEST_STRINGIFICATION(NoMemory);
TEST_STRINGIFICATION(InvalidInput);
TEST_STRINGIFICATION(EmptyInput);
TEST_STRINGIFICATION(IncompleteInput);
TEST_STRINGIFICATION(InvalidInput);
TEST_STRINGIFICATION(NoMemory);
TEST_STRINGIFICATION(NotSupported);
TEST_STRINGIFICATION(TooDeep);
}
SECTION("as boolean") {
TEST_BOOLIFICATION(Ok, false);
TEST_BOOLIFICATION(TooDeep, true);
TEST_BOOLIFICATION(NoMemory, true);
TEST_BOOLIFICATION(InvalidInput, true);
TEST_BOOLIFICATION(EmptyInput, true);
TEST_BOOLIFICATION(IncompleteInput, true);
TEST_BOOLIFICATION(InvalidInput, true);
TEST_BOOLIFICATION(NoMemory, true);
TEST_BOOLIFICATION(NotSupported, true);
TEST_BOOLIFICATION(TooDeep, true);
}
SECTION("ostream DeserializationError") {
@ -58,13 +60,6 @@ TEST_CASE("DeserializationError") {
REQUIRE(s.str() == "InvalidInput");
}
SECTION("out of range") {
int code = 666;
DeserializationError err(
*reinterpret_cast<DeserializationError::Code*>(&code));
REQUIRE(err.c_str() == std::string("???"));
}
SECTION("switch") {
DeserializationError err = DeserializationError::InvalidInput;
switch (err.code()) {

View File

@ -56,7 +56,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") {
deserializeJson(doc, " [ \"1234567\" ] ");
REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(8) == doc.memoryUsage());
REQUIRE(JSON_ARRAY_SIZE(1) + JSON_STRING_SIZE(7) == doc.memoryUsage());
// note: we use a string of 8 bytes to be sure that the StaticMemoryPool
// will not insert bytes to enforce alignement
}

View File

@ -62,6 +62,15 @@ TEST_CASE("Filtering") {
"null",
0
},
{
// Member is a string, but filter wants an array
"{\"example\":\"example\"}",
"{\"example\":[true]}",
10,
DeserializationError::Ok,
"{\"example\":null}",
JSON_OBJECT_SIZE(1) + 8
},
{
// Input is an array, but filter wants an object
"[\"hello\",\"world\"]",
@ -214,6 +223,15 @@ TEST_CASE("Filtering") {
"{\"example\":{\"outcome\":42}}",
2 * JSON_OBJECT_SIZE(1) + 16
},
{
// wildcard
"{\"example\":{\"type\":\"int\",\"outcome\":42}}",
"{\"*\":{\"outcome\":true}}",
10,
DeserializationError::Ok,
"{\"example\":{\"outcome\":42}}",
2 * JSON_OBJECT_SIZE(1) + 16
},
{
// only the first element of array counts
"[1,2,3]",
@ -239,7 +257,7 @@ TEST_CASE("Filtering") {
10,
DeserializationError::Ok,
"[{\"example\":1},{\"example\":3}]",
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8
},
{
"[',2,3]",
@ -525,7 +543,7 @@ TEST_CASE("Filtering") {
10,
DeserializationError::InvalidInput,
"{}",
JSON_OBJECT_SIZE(0) + 8
JSON_OBJECT_SIZE(0)
},
{
// incomplete comment after key
@ -534,7 +552,7 @@ TEST_CASE("Filtering") {
10,
DeserializationError::IncompleteInput,
"{}",
JSON_OBJECT_SIZE(0) + 8
JSON_OBJECT_SIZE(0)
},
{
// invalid comment after colon
@ -730,20 +748,3 @@ TEST_CASE("Overloads") {
}
#endif
}
TEST_CASE("StringMover::reclaim()") {
StaticJsonDocument<200> filter;
filter["a"] = true;
filter["c"] = true;
char input[] = "{\"a\":1,\"b\":2,\"c\":1}";
StaticJsonDocument<200> doc;
deserializeJson(doc, input, DeserializationOption::Filter(filter));
REQUIRE(doc.as<std::string>() == "{\"a\":1,\"c\":1}");
CHECK(input[0] == 'a');
CHECK(input[1] == 0);
CHECK(input[2] == 'c');
CHECK(input[3] == 0);
}

View File

@ -3,11 +3,27 @@
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <sstream>
#include "CustomReader.hpp"
TEST_CASE("deserializeJson(char*)") {
StaticJsonDocument<1024> doc;
SECTION("should not duplicate strings") {
char input[] = "{\"hello\":\"world\"}";
DeserializationError err = deserializeJson(doc, input);
REQUIRE(err == DeserializationError::Ok);
CHECK(doc.memoryUsage() == JSON_OBJECT_SIZE(1));
CHECK(doc.as<JsonVariant>().memoryUsage() ==
JSON_OBJECT_SIZE(1)); // issue #1318
}
}
TEST_CASE("deserializeJson(const std::string&)") {
DynamicJsonDocument doc(4096);
@ -126,3 +142,57 @@ TEST_CASE("deserializeJson(CustomReader)") {
REQUIRE(doc[0] == 4);
REQUIRE(doc[1] == 2);
}
TEST_CASE("deserializeJson(JsonDocument&, MemberProxy)") {
DynamicJsonDocument doc1(4096);
doc1["payload"] = "[4,2]";
DynamicJsonDocument doc2(4096);
DeserializationError err = deserializeJson(doc2, doc1["payload"]);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc2.size() == 2);
REQUIRE(doc2[0] == 4);
REQUIRE(doc2[1] == 2);
}
TEST_CASE("deserializeJson(JsonDocument&, JsonVariant)") {
DynamicJsonDocument doc1(4096);
doc1["payload"] = "[4,2]";
DynamicJsonDocument doc2(4096);
DeserializationError err =
deserializeJson(doc2, doc1["payload"].as<JsonVariant>());
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc2.size() == 2);
REQUIRE(doc2[0] == 4);
REQUIRE(doc2[1] == 2);
}
TEST_CASE("deserializeJson(JsonDocument&, JsonVariantConst)") {
DynamicJsonDocument doc1(4096);
doc1["payload"] = "[4,2]";
DynamicJsonDocument doc2(4096);
DeserializationError err =
deserializeJson(doc2, doc1["payload"].as<JsonVariantConst>());
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc2.size() == 2);
REQUIRE(doc2[0] == 4);
REQUIRE(doc2[1] == 2);
}
TEST_CASE("deserializeJson(JsonDocument&, ElementProxy)") {
DynamicJsonDocument doc1(4096);
doc1[0] = "[4,2]";
DynamicJsonDocument doc2(4096);
DeserializationError err = deserializeJson(doc2, doc1[0]);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc2.size() == 2);
REQUIRE(doc2[0] == 4);
REQUIRE(doc2[1] == 2);
}

View File

@ -27,7 +27,13 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") {
SECTION("Empty input") {
DeserializationError err = deserializeJson(doc, "");
REQUIRE(err == DeserializationError::IncompleteInput);
REQUIRE(err == DeserializationError::EmptyInput);
}
SECTION("Only spaces") {
DeserializationError err = deserializeJson(doc, " \t\n\r");
REQUIRE(err == DeserializationError::EmptyInput);
}
SECTION("issue #628") {

View File

@ -290,4 +290,10 @@ TEST_CASE("deserialize JSON object") {
REQUIRE(obj.size() == 0);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
}
SECTION("Issue #1335") {
std::string json("{\"a\":{},\"b\":{}}");
deserializeJson(doc, json);
CHECK(doc.as<std::string>() == json);
}
}

View File

@ -74,16 +74,16 @@ TEST_CASE("Invalid JSON string") {
}
}
TEST_CASE("Not enough room to duplicate the string") {
DynamicJsonDocument doc(JSON_OBJECT_SIZE(0));
TEST_CASE("Not enough room to save the key") {
DynamicJsonDocument doc(JSON_OBJECT_SIZE(1) + 8);
SECTION("Quoted string") {
REQUIRE(deserializeJson(doc, "{\"example\":1}") ==
REQUIRE(deserializeJson(doc, "{\"accuracy\":1}") ==
DeserializationError::NoMemory);
}
SECTION("Non-quoted string") {
REQUIRE(deserializeJson(doc, "{example:1}") ==
REQUIRE(deserializeJson(doc, "{accuracy:1}") ==
DeserializationError::NoMemory);
}
}

View File

@ -11,6 +11,7 @@ add_executable(JsonDocumentTests
DynamicJsonDocument.cpp
isNull.cpp
nesting.cpp
overflowed.cpp
remove.cpp
shrinkToFit.cpp
size.cpp
@ -18,9 +19,4 @@ add_executable(JsonDocumentTests
subscript.cpp
)
target_link_libraries(JsonDocumentTests
ArduinoJson
catch
)
add_test(JsonDocument JsonDocumentTests)

View File

@ -212,7 +212,7 @@ TEST_CASE("StaticJsonDocument") {
SECTION("garbageCollect()") {
StaticJsonDocument<256> doc;
doc[std::string("example")] = std::string("example");
doc[std::string("example")] = std::string("jukebox");
doc.remove("example");
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 16);

View File

@ -0,0 +1,79 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonDocument::overflowed()") {
SECTION("returns false on a fresh object") {
StaticJsonDocument<0> doc;
CHECK(doc.overflowed() == false);
}
SECTION("returns true after a failed insertion") {
StaticJsonDocument<0> doc;
doc.add(0);
CHECK(doc.overflowed() == true);
}
SECTION("returns false after successful insertion") {
StaticJsonDocument<JSON_ARRAY_SIZE(1)> doc;
doc.add(0);
CHECK(doc.overflowed() == false);
}
SECTION("returns true after a failed string copy") {
StaticJsonDocument<JSON_ARRAY_SIZE(1)> doc;
doc.add(std::string("example"));
CHECK(doc.overflowed() == true);
}
SECTION("returns false after a successful string copy") {
StaticJsonDocument<JSON_ARRAY_SIZE(1) + 8> doc;
doc.add(std::string("example"));
CHECK(doc.overflowed() == false);
}
SECTION("returns true after a failed deserialization") {
StaticJsonDocument<JSON_ARRAY_SIZE(1)> doc;
deserializeJson(doc, "[\"example\"]");
CHECK(doc.overflowed() == true);
}
SECTION("returns false after a successful deserialization") {
StaticJsonDocument<JSON_ARRAY_SIZE(1) + 8> doc;
deserializeJson(doc, "[\"example\"]");
CHECK(doc.overflowed() == false);
}
SECTION("returns false after clear()") {
StaticJsonDocument<0> doc;
doc.add(0);
doc.clear();
CHECK(doc.overflowed() == false);
}
SECTION("remains false after shrinkToFit()") {
DynamicJsonDocument doc(JSON_ARRAY_SIZE(1));
doc.add(0);
doc.shrinkToFit();
CHECK(doc.overflowed() == false);
}
SECTION("remains true after shrinkToFit()") {
DynamicJsonDocument doc(JSON_ARRAY_SIZE(1));
doc.add(0);
doc.add(0);
doc.shrinkToFit();
CHECK(doc.overflowed() == true);
}
SECTION("return false after garbageCollect()") {
DynamicJsonDocument doc(JSON_ARRAY_SIZE(1));
doc.add(0);
doc.add(0);
doc.garbageCollect();
CHECK(doc.overflowed() == false);
}
}

View File

@ -94,8 +94,8 @@ TEST_CASE("BasicJsonDocument::shrinkToFit()") {
}
SECTION("owned raw") {
doc.set(serialized(std::string("[{},123]")));
testShrinkToFit(doc, "[{},123]", 8);
doc.set(serialized(std::string("[{},12]")));
testShrinkToFit(doc, "[{},12]", 8);
}
SECTION("linked key") {

View File

@ -19,9 +19,4 @@ add_executable(JsonObjectTests
subscript.cpp
)
target_link_libraries(JsonObjectTests
ArduinoJson
catch
)
add_test(JsonObject JsonObjectTests)

View File

@ -107,43 +107,43 @@ TEST_CASE("JsonObject::operator[]") {
SECTION("should duplicate char* value") {
obj["hello"] = const_cast<char*>("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate char* key") {
obj[const_cast<char*>("hello")] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate char* key&value") {
obj[const_cast<char*>("hello")] = const_cast<char*>("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(5);
REQUIRE(expectedSize <= doc.memoryUsage());
}
SECTION("should duplicate std::string value") {
obj["hello"] = std::string("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string key") {
obj[std::string("hello")] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}
SECTION("should duplicate std::string key&value") {
obj[std::string("hello")] = std::string("world");
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + 2 * JSON_STRING_SIZE(5);
REQUIRE(expectedSize <= doc.memoryUsage());
}
SECTION("should duplicate a non-static JsonString key") {
obj[JsonString("hello", false)] = "world";
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(6);
const size_t expectedSize = JSON_OBJECT_SIZE(1) + JSON_STRING_SIZE(5);
REQUIRE(expectedSize == doc.memoryUsage());
}

View File

@ -14,9 +14,4 @@ add_executable(JsonSerializerTests
std_string.cpp
)
target_link_libraries(JsonSerializerTests
ArduinoJson
catch
)
add_test(JsonSerializer JsonSerializerTests)

View File

@ -24,9 +24,4 @@ add_executable(JsonVariantTests
undefined.cpp
)
target_link_libraries(JsonVariantTests
ArduinoJson
catch
)
add_test(JsonVariant JsonVariantTests)

View File

@ -10,6 +10,8 @@ namespace my {
using ARDUINOJSON_NAMESPACE::isinf;
} // namespace my
enum MY_ENUM { ONE = 1, TWO = 2 };
TEST_CASE("JsonVariant::as()") {
static const char* null = 0;
@ -212,4 +214,10 @@ TEST_CASE("JsonVariant::as()") {
REQUIRE(cvar.as<char*>() == std::string("hello"));
// REQUIRE(cvar.as<std::string>() == std::string("hello"));
}
SECTION("as<enum>()") {
variant.set(1);
REQUIRE(variant.as<MY_ENUM>() == ONE);
}
}

View File

@ -5,460 +5,276 @@
#include <ArduinoJson.h>
#include <catch.hpp>
template <typename T>
void checkEquals(T a, T b) {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(a);
// Most code is already covered by arithmeticCompare.cpp.
// Here, we're just filling the holes
REQUIRE(b == variant);
REQUIRE(variant == b);
REQUIRE(b <= variant);
REQUIRE(variant <= b);
REQUIRE(b >= variant);
REQUIRE(variant >= b);
REQUIRE_FALSE(b != variant);
REQUIRE_FALSE(variant != b);
REQUIRE_FALSE(b > variant);
REQUIRE_FALSE(variant > b);
REQUIRE_FALSE(b < variant);
REQUIRE_FALSE(variant < b);
}
template <typename T>
void checkGreater(T a, T b) {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(a);
REQUIRE(variant > b);
REQUIRE(b < variant);
REQUIRE(variant != b);
REQUIRE(b != variant);
REQUIRE_FALSE(variant < b);
REQUIRE_FALSE(b > variant);
REQUIRE_FALSE(variant == b);
REQUIRE_FALSE(b == variant);
}
template <typename T>
void checkLower(T a, T b) {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(a);
REQUIRE(variant < b);
REQUIRE(b > variant);
REQUIRE(variant != b);
REQUIRE(b != variant);
REQUIRE_FALSE(variant > b);
REQUIRE_FALSE(b < variant);
REQUIRE_FALSE(variant == b);
REQUIRE_FALSE(b == variant);
}
template <typename T>
void checkComparisons(T low, T mid, T high) {
checkEquals(mid, mid);
checkGreater(mid, low);
checkLower(mid, high);
}
TEST_CASE("JsonVariant comparisons") {
static const char* null = 0;
SECTION("Double") {
checkComparisons<double>(123.44, 123.45, 123.46);
}
SECTION("Float") {
checkComparisons<float>(123.44f, 123.45f, 123.46f);
}
SECTION("SChar") {
checkComparisons<signed char>(122, 123, 124);
}
SECTION("SInt") {
checkComparisons<signed int>(122, 123, 124);
}
SECTION("SLong") {
checkComparisons<signed long>(122L, 123L, 124L);
}
SECTION("SShort") {
checkComparisons<signed short>(122, 123, 124);
}
SECTION("UChar") {
checkComparisons<unsigned char>(122, 123, 124);
}
SECTION("UInt") {
checkComparisons<unsigned int>(122, 123, 124);
}
SECTION("ULong") {
checkComparisons<unsigned long>(122L, 123L, 124L);
}
SECTION("UShort") {
checkComparisons<unsigned short>(122, 123, 124);
}
SECTION("null") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set(null);
REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant);
REQUIRE(variant == null);
REQUIRE_FALSE(variant != null);
REQUIRE(variant != "null");
REQUIRE_FALSE(variant == "null");
}
SECTION("StringLiteral") {
DynamicJsonDocument doc(4096);
deserializeJson(doc, "\"hello\"");
JsonVariant variant = doc.as<JsonVariant>();
REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant);
REQUIRE(variant == "hello");
REQUIRE_FALSE(variant != "hello");
REQUIRE(variant != "world");
REQUIRE_FALSE(variant == "world");
REQUIRE(variant != null);
REQUIRE_FALSE(variant == null);
REQUIRE("hello" == variant);
REQUIRE_FALSE("hello" != variant);
REQUIRE("world" != variant);
REQUIRE_FALSE("world" == variant);
REQUIRE(null != variant);
REQUIRE_FALSE(null == variant);
}
SECTION("String") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set("hello");
REQUIRE(variant == variant);
REQUIRE_FALSE(variant != variant);
REQUIRE(variant == std::string("hello"));
REQUIRE_FALSE(variant != std::string("hello"));
REQUIRE(variant != std::string("world"));
REQUIRE_FALSE(variant == std::string("world"));
REQUIRE(variant != null);
REQUIRE_FALSE(variant == null);
REQUIRE(std::string("hello") == variant);
REQUIRE_FALSE(std::string("hello") != variant);
REQUIRE(std::string("world") != variant);
REQUIRE_FALSE(std::string("world") == variant);
REQUIRE(null != variant);
REQUIRE_FALSE(null == variant);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("VLA equals") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set("hello");
REQUIRE((vla == variant));
REQUIRE((variant == vla));
REQUIRE_FALSE((vla != variant));
REQUIRE_FALSE((variant != vla));
}
SECTION("VLA differs") {
int i = 16;
char vla[i];
strcpy(vla, "hello");
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
variant.set("world");
REQUIRE((vla != variant));
REQUIRE((variant != vla));
REQUIRE_FALSE((vla == variant));
REQUIRE_FALSE((variant == vla));
}
#endif
DynamicJsonDocument doc1(4096), doc2(4096), doc3(4096);
JsonVariant variant1 = doc1.to<JsonVariant>();
JsonVariant variant2 = doc2.to<JsonVariant>();
JsonVariant variant3 = doc3.to<JsonVariant>();
SECTION("Variants containing integers") {
variant1.set(42);
variant2.set(42);
variant3.set(666);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing linked strings") {
// create two identical strings at different addresses
char hello1[] = "hello";
char hello2[] = "hello";
REQUIRE(hello1 != hello2);
variant1.set(hello1);
variant2.set(hello2);
variant3.set("world");
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing owned strings") {
variant1.set(std::string("hello"));
variant2.set(std::string("hello"));
variant3.set(std::string("world"));
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing linked raws") {
// create two identical strings at different addresses
char hello1[] = "hello";
char hello2[] = "hello";
REQUIRE(hello1 != hello2);
variant1.set(serialized(hello1));
variant2.set(serialized(hello2));
variant3.set(serialized("world"));
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing owned raws") {
variant1.set(serialized(std::string("hello")));
variant2.set(serialized(std::string("hello")));
variant3.set(serialized(std::string("world")));
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("Variants containing mixed strings (issue #1051)") {
variant1.set("hello");
variant2.set(std::string("hello"));
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant2 == variant1);
REQUIRE_FALSE(variant2 != variant1);
}
SECTION("Variants containing double") {
variant1.set(42.0);
variant2.set(42.0);
variant3.set(666.0);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("BoolInVariant") {
variant1.set(true);
variant2.set(true);
variant3.set(false);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("ArrayInVariant") {
JsonArray array1 = variant1.to<JsonArray>();
JsonArray array2 = variant2.to<JsonArray>();
array1.add(42);
array2.add(42);
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
SECTION("ObjectInVariant") {
JsonObject obj1 = variant1.to<JsonObject>();
JsonObject obj2 = variant2.to<JsonObject>();
obj1["hello"] = "world";
obj2["hello"] = "world";
REQUIRE(variant1 == variant2);
REQUIRE_FALSE(variant1 != variant2);
REQUIRE(variant1 != variant3);
REQUIRE_FALSE(variant1 == variant3);
}
}
class VariantComparisionFixture {
private:
TEST_CASE("Compare JsonVariant with value") {
StaticJsonDocument<256> doc;
JsonVariant variant;
JsonVariant a = doc.addElement();
public:
VariantComparisionFixture() : variant(doc.to<JsonVariant>()) {}
SECTION("null vs (char*)0") {
char* b = 0;
protected:
template <typename T>
void setValue(const T& value) {
variant.set(value);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
template <typename T>
void assertEqualsTo(const T& value) {
REQUIRE(variant == value);
REQUIRE(value == variant);
SECTION("42 vs 42") {
a.set(42);
int b = 42;
REQUIRE_FALSE(variant != value);
REQUIRE_FALSE(value != variant);
}
template <typename T>
void assertDiffersFrom(const T& value) {
REQUIRE(variant != value);
REQUIRE(value != variant);
REQUIRE_FALSE(variant == value);
REQUIRE_FALSE(value == variant);
}
template <typename T>
void assertGreaterThan(const T& value) {
REQUIRE((variant > value));
REQUIRE((variant >= value));
REQUIRE(value < variant);
REQUIRE(value <= variant);
REQUIRE_FALSE((variant < value));
REQUIRE_FALSE((variant <= value));
REQUIRE_FALSE(value > variant);
REQUIRE_FALSE(value >= variant);
}
template <typename T>
void assertLowerThan(const T& value) {
REQUIRE(variant < value);
REQUIRE(variant <= value);
REQUIRE(value > variant);
REQUIRE(value >= variant);
REQUIRE_FALSE(variant > value);
REQUIRE_FALSE(variant >= value);
REQUIRE_FALSE(value < variant);
REQUIRE_FALSE(value <= variant);
}
};
TEST_CASE_METHOD(VariantComparisionFixture,
"Compare variant with another type") {
SECTION("null") {
assertDiffersFrom(3);
assertDiffersFrom("world");
}
SECTION("string") {
setValue("hello");
assertEqualsTo("hello");
assertDiffersFrom(3);
assertDiffersFrom("world");
assertGreaterThan("helln");
assertLowerThan("hellp");
}
SECTION("positive integer") {
setValue(42);
assertEqualsTo(42);
assertDiffersFrom(43);
assertGreaterThan(41);
assertLowerThan(43);
assertDiffersFrom("world");
}
SECTION("negative integer") {
setValue(-42);
assertEqualsTo(-42);
assertDiffersFrom(42);
assertGreaterThan(-43);
assertLowerThan(-41);
assertDiffersFrom("world");
}
SECTION("double") {
setValue(42.0);
assertEqualsTo(42.0);
assertDiffersFrom(42.1);
assertGreaterThan(41.0);
assertLowerThan(43.0);
assertDiffersFrom("42.0");
}
SECTION("true") {
setValue(true);
assertEqualsTo(true);
assertDiffersFrom(false);
assertDiffersFrom(1);
assertDiffersFrom("true");
assertDiffersFrom(1.0);
assertGreaterThan(false);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
}
TEST_CASE("Compare JsonVariant with JsonVariant") {
StaticJsonDocument<256> doc;
JsonVariant a = doc.addElement();
JsonVariant b = doc.addElement();
SECTION("'abc' vs 'abc'") {
a.set("abc");
b.set("abc");
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("'abc' vs 'bcd'") {
a.set("abc");
b.set("bcd");
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("'bcd' vs 'abc'") {
a.set("bcd");
b.set("abc");
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("serialized('abc') vs serialized('abc')") {
a.set(serialized("abc"));
b.set(serialized("abc"));
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("serialized('abc') vs serialized('bcd')") {
a.set(serialized("abc"));
b.set(serialized("bcd"));
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("serialized('bcd') vs serialized('abc')") {
a.set(serialized("bcd"));
b.set(serialized("abc"));
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("false vs true") {
a.set(false);
b.set(true);
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("false vs -1") {
a.set(false);
b.set(-1);
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("null vs null") {
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42 vs 42") {
a.set(42);
b.set(42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42 vs 42.0") {
a.set(42);
b.set(42.0);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("42.0 vs 42") {
a.set(42.0);
b.set(42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("-42 vs -42") {
a.set(-42);
b.set(-42);
CHECK(a == b);
CHECK(a <= b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("-42 vs 42") {
a.set(-42);
b.set(42);
CHECK(a != b);
CHECK(a < b);
CHECK(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("42 vs -42") {
a.set(42);
b.set(-42);
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("42.0 vs -42") {
a.set(42.0);
b.set(-42);
CHECK(a != b);
CHECK(a > b);
CHECK(a >= b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
}
SECTION("[1] vs [1]") {
a.add(1);
b.add(1);
CHECK(a <= b);
CHECK(a == b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("[1] vs [2]") {
a.add(1);
b.add(2);
CHECK(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
SECTION("{x:1} vs {x:1}") {
a["x"] = 1;
b["x"] = 1;
CHECK(a <= b);
CHECK(a == b);
CHECK(a >= b);
CHECK_FALSE(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a > b);
}
SECTION("{x:1} vs {x:2}") {
a["x"] = 1;
b["x"] = 2;
CHECK(a != b);
CHECK_FALSE(a < b);
CHECK_FALSE(a <= b);
CHECK_FALSE(a == b);
CHECK_FALSE(a > b);
CHECK_FALSE(a >= b);
}
}

View File

@ -47,20 +47,20 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
var1.set(str);
var2.set(var1);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
}
SECTION("stores std::string by copy") {
var1.set(std::string("hello!!"));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
}
SECTION("stores Serialized<const char*> by reference") {
var1.set(serialized("hello!!", JSON_STRING_SIZE(8)));
var1.set(serialized("hello!!", 8));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == 0);
@ -69,18 +69,18 @@ TEST_CASE("JsonVariant::set(JsonVariant)") {
SECTION("stores Serialized<char*> by copy") {
char str[] = "hello!!";
var1.set(serialized(str, 8));
var1.set(serialized(str, 7));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
}
SECTION("stores Serialized<std::string> by copy") {
var1.set(serialized(std::string("hello!!!")));
var1.set(serialized(std::string("hello!!")));
var2.set(var1);
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(8));
REQUIRE(doc1.memoryUsage() == JSON_STRING_SIZE(7));
REQUIRE(doc2.memoryUsage() == JSON_STRING_SIZE(7));
}
}

View File

@ -5,6 +5,8 @@
#include <ArduinoJson.h>
#include <catch.hpp>
enum MYENUM2 { ONE = 1, TWO = 2 };
template <typename TVariant>
void checkIsArray(TVariant var) {
REQUIRE(var.template is<JsonArray>());
@ -80,6 +82,7 @@ void checkIsInteger(TVariant var) {
REQUIRE(var.template is<int>());
REQUIRE(var.template is<float>());
REQUIRE(var.template is<double>());
REQUIRE(var.template is<MYENUM2>());
REQUIRE_FALSE(var.template is<bool>());
REQUIRE_FALSE(var.template is<const char *>());
@ -107,6 +110,7 @@ void checkIsString(TVariant var) {
REQUIRE_FALSE(var.template is<double>());
REQUIRE_FALSE(var.template is<float>());
REQUIRE_FALSE(var.template is<long>());
REQUIRE_FALSE(var.template is<MYENUM2>());
REQUIRE_FALSE(var.template is<JsonArray>());
REQUIRE_FALSE(var.template is<JsonObject>());
}

View File

@ -7,7 +7,7 @@
TEST_CASE("JsonVariant::operator|()") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
JsonVariant variant = doc["value"].to<JsonVariant>();
SECTION("undefined") {
SECTION("undefined | const char*") {
@ -24,6 +24,27 @@ TEST_CASE("JsonVariant::operator|()") {
bool result = variant | true;
REQUIRE(result == true);
}
SECTION("undefined | ElementProxy") {
doc["array"][0] = 42;
JsonVariantConst result = variant | doc["array"][0];
REQUIRE(result == 42);
}
SECTION("undefined | MemberProxy") {
doc["other"] = 42;
JsonVariantConst result = variant | doc["other"];
REQUIRE(result == 42);
}
SECTION("ElementProxy | ElementProxy") {
doc["array"][0] = 42;
JsonVariantConst result = doc["array"][1] | doc["array"][0];
REQUIRE(result == 42);
}
}
SECTION("null") {
@ -43,6 +64,20 @@ TEST_CASE("JsonVariant::operator|()") {
bool result = variant | true;
REQUIRE(result == true);
}
SECTION("null | ElementProxy") {
doc["array"][0] = 42;
JsonVariantConst result = variant | doc["array"][0];
REQUIRE(result == 42);
}
SECTION("null | MemberProxy") {
doc["other"] = 42;
JsonVariantConst result = variant | doc["other"];
REQUIRE(result == 42);
}
}
SECTION("int | const char*") {
@ -57,6 +92,20 @@ TEST_CASE("JsonVariant::operator|()") {
REQUIRE(result == 42);
}
SECTION("int | ElementProxy") {
variant.set(42);
doc["array"][0] = 666;
JsonVariantConst result = variant | doc["array"][0];
REQUIRE(result == 42);
}
SECTION("int | MemberProxy") {
variant.set(42);
doc["other"] = 666;
JsonVariantConst result = variant | doc["other"];
REQUIRE(result == 42);
}
SECTION("int | int") {
variant.set(0);
int result = variant | 666;

View File

@ -7,115 +7,150 @@
enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };
TEST_CASE("JsonVariant and strings") {
TEST_CASE("JsonVariant::set() when there is enough memory") {
DynamicJsonDocument doc(4096);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("stores const char* by reference") {
SECTION("const char*") {
char str[16];
strcpy(str, "hello");
variant.set(static_cast<const char *>(str));
bool result = variant.set(static_cast<const char *>(str));
strcpy(str, "world");
REQUIRE(variant == "world");
REQUIRE(result == true);
REQUIRE(variant == "world"); // stores by pointer
}
SECTION("stores char* by copy") {
char str[16];
SECTION("(const char*)0") {
bool result = variant.set(static_cast<const char *>(0));
strcpy(str, "hello");
variant.set(str);
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant.isNull());
}
SECTION("stores unsigned char* by copy") {
SECTION("char*") {
char str[16];
strcpy(str, "hello");
variant.set(reinterpret_cast<unsigned char *>(str));
bool result = variant.set(str);
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
SECTION("stores signed char* by copy") {
SECTION("(char*)0") {
bool result = variant.set(static_cast<char *>(0));
REQUIRE(result == true);
REQUIRE(variant.isNull());
}
SECTION("unsigned char*") {
char str[16];
strcpy(str, "hello");
variant.set(reinterpret_cast<signed char *>(str));
bool result = variant.set(reinterpret_cast<unsigned char *>(str));
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
SECTION("signed char*") {
char str[16];
strcpy(str, "hello");
bool result = variant.set(reinterpret_cast<signed char *>(str));
strcpy(str, "world");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("stores VLA by copy") {
SECTION("VLA") {
int n = 16;
char str[n];
strcpy(str, "hello");
variant.set(str);
bool result = variant.set(str);
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
#endif
SECTION("stores std::string by copy") {
SECTION("std::string") {
std::string str;
str = "hello";
variant.set(str);
bool result = variant.set(str);
str.replace(0, 5, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
SECTION("stores static JsonString by reference") {
SECTION("static JsonString") {
char str[16];
strcpy(str, "hello");
variant.set(JsonString(str, true));
bool result = variant.set(JsonString(str, true));
strcpy(str, "world");
REQUIRE(variant == "world");
REQUIRE(result == true);
REQUIRE(variant == "world"); // stores by pointer
}
SECTION("stores non-static JsonString by copy") {
SECTION("non-static JsonString") {
char str[16];
strcpy(str, "hello");
variant.set(JsonString(str, false));
bool result = variant.set(JsonString(str, false));
strcpy(str, "world");
REQUIRE(variant == "hello");
REQUIRE(result == true);
REQUIRE(variant == "hello"); // stores by copy
}
SECTION("stores an enum as an integer") {
SECTION("enum") {
ErrorCode code = ERROR_10;
variant.set(code);
bool result = variant.set(code);
REQUIRE(result == true);
REQUIRE(variant.is<int>() == true);
REQUIRE(variant.as<int>() == 10);
}
}
TEST_CASE("JsonVariant with not enough memory") {
TEST_CASE("JsonVariant::set() with not enough memory") {
StaticJsonDocument<1> doc;
JsonVariant v = doc.to<JsonVariant>();
SECTION("std::string") {
v.set(std::string("hello world!!"));
bool result = v.set(std::string("hello world!!"));
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("Serialized<std::string>") {
v.set(serialized(std::string("hello world!!")));
bool result = v.set(serialized(std::string("hello world!!")));
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("char*") {
char s[] = "hello world!!";
bool result = v.set(s);
REQUIRE(result == false);
REQUIRE(v.isNull());
}
}

View File

@ -13,9 +13,4 @@ add_executable(MemberProxyTests
subscript.cpp
)
target_link_libraries(MemberProxyTests
ArduinoJson
catch
)
add_test(MemberProxy MemberProxyTests)

View File

@ -10,17 +10,39 @@ using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemberProxy::operator==()") {
DynamicJsonDocument doc(4096);
SECTION("same values") {
doc["key1"] = "value";
doc["key2"] = "value";
REQUIRE(doc["key1"] == doc["key2"]);
REQUIRE_FALSE(doc["key1"] != doc["key2"]);
SECTION("1 vs 1") {
doc["a"] = 1;
doc["b"] = 1;
REQUIRE(doc["a"] <= doc["b"]);
REQUIRE(doc["a"] == doc["b"]);
REQUIRE(doc["a"] >= doc["b"]);
REQUIRE_FALSE(doc["a"] != doc["b"]);
REQUIRE_FALSE(doc["a"] < doc["b"]);
REQUIRE_FALSE(doc["a"] > doc["b"]);
}
SECTION("different values") {
doc["key1"] = "value1";
doc["key2"] = "value2";
REQUIRE_FALSE(doc["key1"] == doc["key2"]);
REQUIRE(doc["key1"] != doc["key2"]);
SECTION("1 vs 2") {
doc["a"] = 1;
doc["b"] = 2;
REQUIRE(doc["a"] != doc["b"]);
REQUIRE(doc["a"] < doc["b"]);
REQUIRE(doc["a"] <= doc["b"]);
REQUIRE_FALSE(doc["a"] == doc["b"]);
REQUIRE_FALSE(doc["a"] > doc["b"]);
REQUIRE_FALSE(doc["a"] >= doc["b"]);
}
SECTION("'abc' vs 'bcd'") {
doc["a"] = "abc";
doc["b"] = "bcd";
REQUIRE(doc["a"] != doc["b"]);
REQUIRE(doc["a"] < doc["b"]);
REQUIRE(doc["a"] <= doc["b"]);
REQUIRE_FALSE(doc["a"] == doc["b"]);
REQUIRE_FALSE(doc["a"] > doc["b"]);
REQUIRE_FALSE(doc["a"] >= doc["b"]);
}
}

View File

@ -4,15 +4,10 @@
add_executable(MemoryPoolTests
allocVariant.cpp
allocString.cpp
clear.cpp
saveString.cpp
size.cpp
StringBuilder.cpp
)
target_link_libraries(MemoryPoolTests
ArduinoJson
catch
StringCopier.cpp
)
add_test(MemoryPool MemoryPoolTests)

View File

@ -1,41 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Memory/StringBuilder.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StringBuilder") {
char buffer[4096];
SECTION("Works when buffer is big enough") {
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
StringBuilder str(&pool);
str.append("hello");
REQUIRE(str.complete() == std::string("hello"));
}
SECTION("Returns null when too small") {
MemoryPool pool(buffer, sizeof(void*));
StringBuilder str(&pool);
str.append("hello world!");
REQUIRE(str.complete() == 0);
}
SECTION("Increases size of memory pool") {
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
StringBuilder str(&pool);
str.append('h');
str.complete();
REQUIRE(JSON_STRING_SIZE(2) == pool.size());
}
}

View File

@ -0,0 +1,84 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/StringStorage/StringCopier.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("StringCopier") {
char buffer[4096];
SECTION("Works when buffer is big enough") {
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
StringCopier str(pool);
str.startString();
str.append("hello");
str.append('\0');
REQUIRE(str.isValid() == true);
REQUIRE(str.c_str() == std::string("hello"));
}
SECTION("Returns null when too small") {
MemoryPool pool(buffer, sizeof(void*));
StringCopier str(pool);
str.startString();
str.append("hello world!");
REQUIRE(str.isValid() == false);
}
SECTION("Increases size of memory pool") {
MemoryPool pool(buffer, addPadding(JSON_STRING_SIZE(6)));
StringCopier str(pool);
str.startString();
str.append('h');
str.save();
REQUIRE(1 == pool.size());
}
}
static const char* addStringToPool(MemoryPool& pool, const char* s) {
StringCopier str(pool);
str.startString();
str.append(s);
str.append('\0');
return str.save();
}
TEST_CASE("StringCopier::save() deduplicates strings") {
char buffer[4096];
MemoryPool pool(buffer, 4096);
SECTION("Basic") {
const char* s1 = addStringToPool(pool, "hello");
const char* s2 = addStringToPool(pool, "world");
const char* s3 = addStringToPool(pool, "hello");
REQUIRE(s1 == s3);
REQUIRE(s2 != s3);
REQUIRE(pool.size() == 12);
}
SECTION("Requires terminator") {
const char* s1 = addStringToPool(pool, "hello world");
const char* s2 = addStringToPool(pool, "hello");
REQUIRE(s2 != s1);
REQUIRE(pool.size() == 12 + 6);
}
SECTION("Don't overrun") {
const char* s1 = addStringToPool(pool, "hello world");
const char* s2 = addStringToPool(pool, "wor");
REQUIRE(s2 != s1);
REQUIRE(pool.size() == 12 + 4);
}
}

View File

@ -1,67 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("MemoryPool::allocFrozenString()") {
const size_t poolCapacity = 64;
const size_t longestString = poolCapacity;
char buffer[poolCapacity];
MemoryPool pool(buffer, poolCapacity);
SECTION("Returns different addresses") {
char *a = pool.allocFrozenString(1);
char *b = pool.allocFrozenString(1);
REQUIRE(a != b);
}
SECTION("Returns NULL when full") {
void *p1 = pool.allocFrozenString(longestString);
REQUIRE(p1 != 0);
void *p2 = pool.allocFrozenString(1);
REQUIRE(p2 == 0);
}
SECTION("Returns NULL when pool is too small") {
void *p = pool.allocFrozenString(longestString + 1);
REQUIRE(0 == p);
}
SECTION("Returns NULL when buffer is NULL") {
MemoryPool pool2(0, poolCapacity);
REQUIRE(0 == pool2.allocFrozenString(2));
}
SECTION("Returns NULL when capacity is 0") {
MemoryPool pool2(buffer, 0);
REQUIRE(0 == pool2.allocFrozenString(2));
}
SECTION("Returns same address after clear()") {
void *a = pool.allocFrozenString(1);
pool.clear();
void *b = pool.allocFrozenString(1);
REQUIRE(a == b);
}
SECTION("Can use full capacity when fresh") {
void *a = pool.allocFrozenString(longestString);
REQUIRE(a != 0);
}
SECTION("Can use full capacity after clear") {
pool.allocFrozenString(longestString);
pool.clear();
void *a = pool.allocFrozenString(longestString);
REQUIRE(a != 0);
}
}

View File

@ -3,6 +3,7 @@
// MIT License
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
@ -21,8 +22,8 @@ TEST_CASE("MemoryPool::clear()") {
}
SECTION("Discards allocated strings") {
pool.allocFrozenString(10);
REQUIRE(pool.size() > 0);
pool.saveString(adaptString(const_cast<char *>("123456789")));
REQUIRE(pool.size() == 10);
pool.clear();

View File

@ -0,0 +1,81 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Strings/StringAdapters.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
static const char *saveString(MemoryPool &pool, const char *s) {
return pool.saveString(adaptString(const_cast<char *>(s)));
}
TEST_CASE("MemoryPool::saveString()") {
char buffer[32];
MemoryPool pool(buffer, 32);
SECTION("Duplicates different strings") {
const char *a = saveString(pool, "hello");
const char *b = saveString(pool, "world");
REQUIRE(a != b);
REQUIRE(pool.size() == 6 + 6);
}
SECTION("Deduplicates identical strings") {
const char *a = saveString(pool, "hello");
const char *b = saveString(pool, "hello");
REQUIRE(a == b);
REQUIRE(pool.size() == 6);
}
SECTION("Returns NULL when full") {
REQUIRE(pool.capacity() == 32);
const void *p1 = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
REQUIRE(p1 != 0);
REQUIRE(pool.size() == 32);
const void *p2 = saveString(pool, "b");
REQUIRE(p2 == 0);
}
SECTION("Returns NULL when pool is too small") {
const void *p = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
REQUIRE(0 == p);
}
SECTION("Returns NULL when buffer is NULL") {
MemoryPool pool2(0, 32);
REQUIRE(0 == saveString(pool2, "a"));
}
SECTION("Returns NULL when capacity is 0") {
MemoryPool pool2(buffer, 0);
REQUIRE(0 == saveString(pool2, "a"));
}
SECTION("Returns same address after clear()") {
const void *a = saveString(pool, "hello");
pool.clear();
const void *b = saveString(pool, "world");
REQUIRE(a == b);
}
SECTION("Can use full capacity when fresh") {
const void *a = saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
REQUIRE(a != 0);
}
SECTION("Can use full capacity after clear") {
saveString(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
pool.clear();
const void *a = saveString(pool, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
REQUIRE(a != 0);
}
}

View File

@ -22,29 +22,6 @@ TEST_CASE("MemoryPool::size()") {
REQUIRE(0 == pool.size());
}
SECTION("size() == capacity() after allocExpandableString()") {
pool.allocExpandableString();
REQUIRE(pool.size() == pool.capacity());
}
SECTION("Decreases after freezeString()") {
StringSlot a = pool.allocExpandableString();
pool.freezeString(a, 1);
REQUIRE(pool.size() == JSON_STRING_SIZE(1));
StringSlot b = pool.allocExpandableString();
pool.freezeString(b, 1);
REQUIRE(pool.size() == 2 * JSON_STRING_SIZE(1));
}
SECTION("Increases after allocFrozenString()") {
pool.allocFrozenString(0);
REQUIRE(pool.size() == JSON_STRING_SIZE(0));
pool.allocFrozenString(0);
REQUIRE(pool.size() == 2 * JSON_STRING_SIZE(0));
}
SECTION("Doesn't grow when memory pool is full") {
const size_t variantCount = sizeof(buffer) / sizeof(VariantSlot);

View File

@ -3,56 +3,20 @@
# MIT License
add_executable(MiscTests
arithmeticCompare.cpp
conflicts.cpp
FloatParts.cpp
JsonString.cpp
Readers.cpp
StringAdapters.cpp
StringWriter.cpp
TypeTraits.cpp
unsigned_char.cpp
Utf8.cpp
Utf16.cpp
Utf8.cpp
version.cpp
)
target_link_libraries(MiscTests
ArduinoJson
catch
)
set_target_properties(MiscTests PROPERTIES UNITY_BUILD OFF)
add_test(Misc MiscTests)
macro(build_should_fail target)
set_target_properties(${target}
PROPERTIES
EXCLUDE_FROM_ALL TRUE
EXCLUDE_FROM_DEFAULT_BUILD TRUE
)
add_test(
NAME
${target}
COMMAND
${CMAKE_COMMAND} --build . --target ${target} --config $<CONFIGURATION>
WORKING_DIRECTORY
${CMAKE_BINARY_DIR}
)
set_tests_properties(${target}
PROPERTIES
WILL_FAIL TRUE)
endmacro()
add_executable(Issue978
Issue978.cpp
)
build_should_fail(Issue978)
add_executable(Issue1189
Issue1189.cpp
)
build_should_fail(Issue1189)

View File

@ -0,0 +1,60 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonString") {
SECTION("Default constructor creates a null JsonString") {
JsonString s;
CHECK(s.isNull() == true);
CHECK(s.c_str() == 0);
CHECK(s.isStatic() == true);
}
SECTION("Compare null with null") {
JsonString a, b;
CHECK(a == b);
CHECK_FALSE(a != b);
}
SECTION("Compare null with non-null") {
JsonString a(0), b("hello");
CHECK_FALSE(a == b);
CHECK(a != b);
}
SECTION("Compare non-null with null") {
JsonString a("hello"), b(0);
CHECK_FALSE(a == b);
CHECK(a != b);
}
SECTION("Compare different strings") {
JsonString a("hello"), b("world");
CHECK_FALSE(a == b);
CHECK(a != b);
}
SECTION("Compare identical by pointer") {
JsonString a("hello"), b("hello");
CHECK(a == b);
CHECK_FALSE(a != b);
}
SECTION("Compare identical by value") {
char s1[] = "hello";
char s2[] = "hello";
JsonString a(s1), b(s2);
CHECK(a == b);
CHECK_FALSE(a != b);
}
}

View File

@ -6,10 +6,11 @@
#include "progmem_emulation.hpp"
#include "weird_strcmp.hpp"
#include <ArduinoJson/Strings/ArduinoStringAdapter.hpp>
#include <ArduinoJson/Strings/ConstRamStringAdapter.hpp>
#include <ArduinoJson/Strings/FlashStringAdapter.hpp>
#include <ArduinoJson/Strings/SizedRamStringAdapter.hpp>
#include <ArduinoJson/Strings/StlStringAdapter.hpp>
#include <ArduinoJson/Strings/StdStringAdapter.hpp>
#include <catch.hpp>
@ -101,7 +102,22 @@ TEST_CASE("FlashStringAdapter") {
TEST_CASE("std::string") {
std::string str("bravo");
StlStringAdapter<std::string> adapter = adaptString(str);
StdStringAdapter<std::string> adapter = adaptString(str);
CHECK(adapter.compare(NULL) > 0);
CHECK(adapter.compare("alpha") > 0);
CHECK(adapter.compare("bravo") == 0);
CHECK(adapter.compare("charlie") < 0);
CHECK(adapter.equals("bravo"));
CHECK_FALSE(adapter.equals("charlie"));
CHECK(adapter.size() == 5);
}
TEST_CASE("Arduino String") {
::String str("bravo");
ArduinoStringAdapter adapter = adaptString(str);
CHECK(adapter.compare(NULL) > 0);
CHECK(adapter.compare("alpha") > 0);
@ -116,7 +132,7 @@ TEST_CASE("std::string") {
TEST_CASE("custom_string") {
custom_string str("bravo");
StlStringAdapter<custom_string> adapter = adaptString(str);
StdStringAdapter<custom_string> adapter = adaptString(str);
CHECK(adapter.compare(NULL) > 0);
CHECK(adapter.compare("alpha") > 0);
@ -145,4 +161,12 @@ TEST_CASE("IsString<T>") {
SECTION("const __FlashStringHelper*") {
CHECK(IsString<const __FlashStringHelper*>::value == true);
}
SECTION("const char*") {
CHECK(IsString<const char*>::value == true);
}
SECTION("const char[]") {
CHECK(IsString<const char[8]>::value == true);
}
}

View File

@ -29,6 +29,24 @@ TEST_CASE("Polyfills/type_traits") {
CHECK(is_const<const char>::value == true);
}
SECTION("is_integral") {
CHECK(is_integral<double>::value == false);
CHECK(is_integral<float>::value == false);
CHECK(is_integral<bool>::value == true);
CHECK(is_integral<char>::value == true);
CHECK(is_integral<signed char>::value == true);
CHECK(is_integral<signed int>::value == true);
CHECK(is_integral<signed long>::value == true);
CHECK(is_integral<signed short>::value == true);
CHECK(is_integral<unsigned char>::value == true);
CHECK(is_integral<unsigned int>::value == true);
CHECK(is_integral<unsigned long>::value == true);
CHECK(is_integral<unsigned short>::value == true);
CHECK(is_integral<UInt>::value == true);
}
SECTION("is_signed") {
CHECK(is_signed<char>::value == true);
CHECK(is_signed<signed char>::value == true);

View File

@ -12,12 +12,14 @@ using namespace ARDUINOJSON_NAMESPACE;
static void testCodepoint(uint32_t codepoint, std::string expected) {
char buffer[4096];
MemoryPool pool(buffer, 4096);
StringBuilder str(&pool);
StringCopier str(pool);
str.startString();
CAPTURE(codepoint);
Utf8::encodeCodepoint(codepoint, str);
REQUIRE(str.complete() == expected);
str.append('\0');
REQUIRE(str.c_str() == expected);
}
TEST_CASE("Utf8::encodeCodepoint()") {

View File

@ -0,0 +1,103 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/Numbers/arithmeticCompare.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("arithmeticCompare()") {
SECTION("int vs uint8_t") {
CHECK((arithmeticCompare<int, uint8_t>(256, 1) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<int, uint8_t>(41, 42) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompare<int, uint8_t>(42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, uint8_t>(43, 42) == COMPARE_RESULT_GREATER));
}
SECTION("unsigned vs int") {
CHECK((arithmeticCompare<unsigned, int>(0, -1) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<unsigned, int>(42, 41) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<unsigned, int>(42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<unsigned, int>(42, 43) == COMPARE_RESULT_LESS));
}
SECTION("float vs int") {
CHECK((arithmeticCompare<float, int>(42, 41) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<float, int>(42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<float, int>(42, 43) == COMPARE_RESULT_LESS));
}
SECTION("int vs unsigned") {
CHECK((arithmeticCompare<int, unsigned>(-1, 0) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompare<int, unsigned>(0, 0) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, unsigned>(1, 0) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<int, unsigned>(42, 41) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<int, unsigned>(42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, unsigned>(42, 43) == COMPARE_RESULT_LESS));
}
SECTION("unsigned vs unsigned") {
CHECK((arithmeticCompare<unsigned, unsigned>(42, 41) ==
COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<unsigned, unsigned>(42, 42) ==
COMPARE_RESULT_EQUAL));
CHECK(
(arithmeticCompare<unsigned, unsigned>(42, 43) == COMPARE_RESULT_LESS));
}
SECTION("bool vs bool") {
CHECK(
(arithmeticCompare<bool, bool>(false, false) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<bool, bool>(true, true) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<bool, bool>(false, true) == COMPARE_RESULT_LESS));
CHECK(
(arithmeticCompare<bool, bool>(true, false) == COMPARE_RESULT_GREATER));
}
SECTION("bool vs int") {
CHECK((arithmeticCompare<bool, int>(false, -1) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<bool, int>(false, 0) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<bool, int>(false, 1) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompare<bool, int>(true, 0) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<bool, int>(true, 1) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<bool, int>(true, 2) == COMPARE_RESULT_LESS));
}
SECTION("bool vs int") {
CHECK((arithmeticCompare<int, bool>(0, false) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, bool>(1, true) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompare<int, bool>(1, false) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompare<int, bool>(0, true) == COMPARE_RESULT_LESS));
}
}
TEST_CASE("arithmeticCompareNegateLeft()") {
SECTION("unsigned vs int") {
CHECK((arithmeticCompareNegateLeft<int>(0, 1) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompareNegateLeft<int>(42, -41) == COMPARE_RESULT_LESS));
CHECK((arithmeticCompareNegateLeft<int>(42, -42) == COMPARE_RESULT_EQUAL));
CHECK(
(arithmeticCompareNegateLeft<int>(42, -43) == COMPARE_RESULT_GREATER));
}
SECTION("unsigned vs unsigned") {
CHECK(
(arithmeticCompareNegateLeft<unsigned>(42, 42) == COMPARE_RESULT_LESS));
}
}
TEST_CASE("arithmeticCompareNegateRight()") {
SECTION("int vs unsigned") {
CHECK((arithmeticCompareNegateRight<int>(1, 0) == COMPARE_RESULT_GREATER));
CHECK(
(arithmeticCompareNegateRight<int>(-41, 42) == COMPARE_RESULT_GREATER));
CHECK((arithmeticCompareNegateRight<int>(-42, 42) == COMPARE_RESULT_EQUAL));
CHECK((arithmeticCompareNegateRight<int>(-43, 42) == COMPARE_RESULT_LESS));
}
SECTION("unsigned vs unsigned") {
CHECK((arithmeticCompareNegateRight<unsigned>(42, 42) ==
COMPARE_RESULT_GREATER));
}
}

View File

@ -18,17 +18,14 @@ add_executable(MixedConfigurationTests
enable_nan_0.cpp
enable_nan_1.cpp
enable_progmem_1.cpp
enable_string_deduplication_0.cpp
enable_string_deduplication_1.cpp
use_double_0.cpp
use_double_1.cpp
use_long_long_0.cpp
use_long_long_1.cpp
)
target_link_libraries(MixedConfigurationTests
ArduinoJson
catch
)
set_target_properties(MixedConfigurationTests PROPERTIES UNITY_BUILD OFF)
add_test(MixedConfiguration MixedConfigurationTests)

View File

@ -373,16 +373,22 @@ TEST_CASE("Comments in objects") {
TEST_CASE("Comments alone") {
DynamicJsonDocument doc(2048);
SECTION("Just a trailing comment") {
SECTION("Just a trailing comment with no line break") {
DeserializationError err = deserializeJson(doc, "// comment");
REQUIRE(err == DeserializationError::IncompleteInput);
}
SECTION("Just a trailing comment with no a break") {
DeserializationError err = deserializeJson(doc, "// comment\n");
REQUIRE(err == DeserializationError::EmptyInput);
}
SECTION("Just a block comment") {
DeserializationError err = deserializeJson(doc, "/*comment*/");
REQUIRE(err == DeserializationError::IncompleteInput);
REQUIRE(err == DeserializationError::EmptyInput);
}
SECTION("Just a slash") {

View File

@ -53,33 +53,33 @@ TEST_CASE("Flash strings") {
}
TEST_CASE("strlen_P") {
CHECK(strlen_P(FC("")) == 0);
CHECK(strlen_P(FC("a")) == 1);
CHECK(strlen_P(FC("ac")) == 2);
CHECK(strlen_P(PSTR("")) == 0);
CHECK(strlen_P(PSTR("a")) == 1);
CHECK(strlen_P(PSTR("ac")) == 2);
}
TEST_CASE("strncmp_P") {
CHECK(strncmp_P("a", FC("b"), 0) == 0);
CHECK(strncmp_P("a", FC("b"), 1) == -1);
CHECK(strncmp_P("b", FC("a"), 1) == 1);
CHECK(strncmp_P("a", FC("a"), 0) == 0);
CHECK(strncmp_P("a", FC("b"), 2) == -1);
CHECK(strncmp_P("b", FC("a"), 2) == 1);
CHECK(strncmp_P("a", FC("a"), 2) == 0);
CHECK(strncmp_P("a", PSTR("b"), 0) == 0);
CHECK(strncmp_P("a", PSTR("b"), 1) == -1);
CHECK(strncmp_P("b", PSTR("a"), 1) == 1);
CHECK(strncmp_P("a", PSTR("a"), 0) == 0);
CHECK(strncmp_P("a", PSTR("b"), 2) == -1);
CHECK(strncmp_P("b", PSTR("a"), 2) == 1);
CHECK(strncmp_P("a", PSTR("a"), 2) == 0);
}
TEST_CASE("strcmp_P") {
CHECK(strcmp_P("a", FC("b")) == -1);
CHECK(strcmp_P("b", FC("a")) == 1);
CHECK(strcmp_P("a", FC("a")) == 0);
CHECK(strcmp_P("aa", FC("ab")) == -1);
CHECK(strcmp_P("ab", FC("aa")) == 1);
CHECK(strcmp_P("aa", FC("aa")) == 0);
CHECK(strcmp_P("a", PSTR("b")) == -1);
CHECK(strcmp_P("b", PSTR("a")) == 1);
CHECK(strcmp_P("a", PSTR("a")) == 0);
CHECK(strcmp_P("aa", PSTR("ab")) == -1);
CHECK(strcmp_P("ab", PSTR("aa")) == 1);
CHECK(strcmp_P("aa", PSTR("aa")) == 0);
}
TEST_CASE("memcpy_P") {
char dst[4];
CHECK(memcpy_P(dst, FC("ABC"), 4) == dst);
CHECK(memcpy_P(dst, PSTR("ABC"), 4) == dst);
CHECK(dst[0] == 'A');
CHECK(dst[1] == 'B');
CHECK(dst[2] == 'C');
@ -165,3 +165,22 @@ TEST_CASE("Reader<const __FlashStringHelper*>") {
REQUIRE(buffer[6] == 'g');
}
}
static void testStringification(DeserializationError error,
std::string expected) {
const __FlashStringHelper* s = error.f_str();
CHECK(reinterpret_cast<const char*>(convertFlashToPtr(s)) == expected);
}
#define TEST_STRINGIFICATION(symbol) \
testStringification(DeserializationError::symbol, #symbol)
TEST_CASE("DeserializationError::f_str()") {
TEST_STRINGIFICATION(Ok);
TEST_STRINGIFICATION(EmptyInput);
TEST_STRINGIFICATION(IncompleteInput);
TEST_STRINGIFICATION(InvalidInput);
TEST_STRINGIFICATION(NoMemory);
TEST_STRINGIFICATION(NotSupported);
TEST_STRINGIFICATION(TooDeep);
}

View File

@ -0,0 +1,125 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include "progmem_emulation.hpp"
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 0
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 0") {
StaticJsonDocument<1024> doc;
SECTION("deserializeJson()") {
SECTION("Deduplicate values") {
deserializeJson(doc, "[\"example\",\"example\"]");
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
SECTION("Deduplicate keys") {
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
CHECK(doc.memoryUsage() ==
2 * JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(2) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
}
SECTION("JsonDocument") {
SECTION("values") {
SECTION("std::string") {
doc.add(std::string("example"));
doc.add(std::string("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
SECTION("char*") {
char value[] = "example";
doc.add(value);
doc.add(value);
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
SECTION("Arduino String") {
doc.add(String("example"));
doc.add(String("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
SECTION("Flash string") {
doc.add(F("example"));
doc.add(F("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 16);
CHECK(doc[0].as<char*>() != doc[1].as<char*>());
}
}
SECTION("keys") {
SECTION("std::string") {
doc[0][std::string("example")] = 1;
doc[1][std::string("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
SECTION("char*") {
char key[] = "example";
doc[0][key] = 1;
doc[1][key] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
SECTION("Arduino String") {
doc[0][String("example")] = 1;
doc[1][String("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
SECTION("Flash string") {
doc[0][F("example")] = 1;
doc[1][F("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 16);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 != key2);
}
}
}
}

View File

@ -0,0 +1,124 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include "progmem_emulation.hpp"
#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
#define ARDUINOJSON_ENABLE_PROGMEM 1
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("ARDUINOJSON_ENABLE_STRING_DEDUPLICATION = 1") {
StaticJsonDocument<1024> doc;
SECTION("deserializeJson()") {
SECTION("Deduplicate values") {
deserializeJson(doc, "[\"example\",\"example\"]");
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
SECTION("Deduplicate keys") {
deserializeJson(doc, "[{\"example\":1},{\"example\":2}]");
CHECK(doc.memoryUsage() ==
2 * JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(2) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
}
SECTION("JsonDocument") {
SECTION("values") {
SECTION("std::string") {
doc.add(std::string("example"));
doc.add(std::string("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
SECTION("char*") {
char value[] = "example";
doc.add(value);
doc.add(value);
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
SECTION("Arduino String") {
doc.add(String("example"));
doc.add(String("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
SECTION("Flash string") {
doc.add(F("example"));
doc.add(F("example"));
CHECK(doc.memoryUsage() == JSON_ARRAY_SIZE(2) + 8);
CHECK(doc[0].as<char*>() == doc[1].as<char*>());
}
}
SECTION("keys") {
SECTION("std::string") {
doc[0][std::string("example")] = 1;
doc[1][std::string("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
SECTION("char*") {
char key[] = "example";
doc[0][key] = 1;
doc[1][key] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
SECTION("Arduino String") {
doc[0][String("example")] = 1;
doc[1][String("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
SECTION("Flash string") {
doc[0][F("example")] = 1;
doc[1][F("example")] = 2;
CHECK(doc.memoryUsage() ==
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8);
const char* key1 = doc[0].as<JsonObject>().begin()->key().c_str();
const char* key2 = doc[1].as<JsonObject>().begin()->key().c_str();
CHECK(key1 == key2);
}
}
}
}

View File

@ -3,28 +3,14 @@
#include <catch.hpp>
template <size_t size_of_long>
std::string get_expected_json();
template <>
std::string get_expected_json<4>() {
return "{\"A\":2899336981,\"B\":2129924785}";
}
template <>
std::string get_expected_json<8>() {
return "{\"A\":123456789123456789,\"B\":987654321987654321}";
}
TEST_CASE("ARDUINOJSON_USE_LONG_LONG == 0") {
DynamicJsonDocument doc(4096);
JsonObject root = doc.to<JsonObject>();
root["A"] = 123456789123456789;
root["B"] = 987654321987654321;
doc["A"] = 42;
doc["B"] = 84;
std::string json;
serializeJson(doc, json);
REQUIRE(json == get_expected_json<sizeof(long)>());
REQUIRE(json == "{\"A\":42,\"B\":84}");
}

View File

@ -8,15 +8,12 @@ add_executable(MsgPackDeserializerTests
deserializeStaticVariant.cpp
deserializeVariant.cpp
doubleToFloat.cpp
filter.cpp
incompleteInput.cpp
input_types.cpp
misc.cpp
nestingLimit.cpp
notSupported.cpp
)
target_link_libraries(MsgPackDeserializerTests
ArduinoJson
catch
)
add_test(MsgPackDeserializer MsgPackDeserializerTests)

View File

@ -7,9 +7,9 @@
template <size_t Capacity>
static void check(const char* input, DeserializationError expected) {
StaticJsonDocument<Capacity> variant;
StaticJsonDocument<Capacity> doc;
DeserializationError error = deserializeMsgPack(variant, input);
DeserializationError error = deserializeMsgPack(doc, input);
CAPTURE(input);
REQUIRE(error == expected);
@ -17,7 +17,7 @@ static void check(const char* input, DeserializationError expected) {
template <size_t Size>
static void checkString(const char* input, DeserializationError expected) {
check<JSON_STRING_SIZE(Size)>(input, expected);
check<Size>(input, expected);
}
TEST_CASE("deserializeMsgPack(StaticJsonDocument&)") {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("deserializeMsgPack() returns EmptyInput") {
StaticJsonDocument<100> doc;
SECTION("from sized buffer") {
DeserializationError err = deserializeMsgPack(doc, "", 0);
REQUIRE(err == DeserializationError::EmptyInput);
}
SECTION("from stream") {
std::istringstream input("");
DeserializationError err = deserializeMsgPack(doc, input);
REQUIRE(err == DeserializationError::EmptyInput);
}
}

View File

@ -11,9 +11,4 @@ add_executable(MsgPackSerializerTests
serializeVariant.cpp
)
target_link_libraries(MsgPackSerializerTests
ArduinoJson
catch
)
add_test(MsgPackSerializer MsgPackSerializerTests)

View File

@ -4,14 +4,10 @@
add_executable(NumbersTests
parseFloat.cpp
parseDouble.cpp
parseInteger.cpp
parseNumber.cpp
)
target_link_libraries(NumbersTests
ArduinoJson
catch
)
add_test(Numbers NumbersTests)

View File

@ -0,0 +1,97 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#define ARDUINOJSON_USE_DOUBLE 1
#define ARDUINOJSON_ENABLE_NAN 1
#define ARDUINOJSON_ENABLE_INFINITY 1
#include <ArduinoJson/Numbers/parseNumber.hpp>
#include <ArduinoJson/Variant/VariantImpl.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
void checkDouble(const char* input, double expected) {
CAPTURE(input);
REQUIRE(parseNumber<double>(input) == Approx(expected));
}
void checkDoubleNaN(const char* input) {
CAPTURE(input);
double result = parseNumber<double>(input);
REQUIRE(result != result);
}
void checkDoubleInf(const char* input, bool negative) {
CAPTURE(input);
double x = parseNumber<double>(input);
if (negative)
REQUIRE(x < 0);
else
REQUIRE(x > 0);
REQUIRE(x == x); // not a NaN
REQUIRE(x * 2 == x); // a property of infinity
}
TEST_CASE("parseNumber<double>()") {
SECTION("Short_NoExponent") {
checkDouble("3.14", 3.14);
checkDouble("-3.14", -3.14);
checkDouble("+3.14", +3.14);
}
SECTION("Short_NoDot") {
checkDouble("1E+308", 1E+308);
checkDouble("-1E+308", -1E+308);
checkDouble("+1E-308", +1E-308);
checkDouble("+1e+308", +1e+308);
checkDouble("-1e-308", -1e-308);
}
SECTION("Max") {
checkDouble(".017976931348623147e+310", 1.7976931348623147e+308);
checkDouble(".17976931348623147e+309", 1.7976931348623147e+308);
checkDouble("1.7976931348623147e+308", 1.7976931348623147e+308);
checkDouble("17.976931348623147e+307", 1.7976931348623147e+308);
checkDouble("179.76931348623147e+306", 1.7976931348623147e+308);
}
SECTION("Min") {
checkDouble(".022250738585072014e-306", 2.2250738585072014e-308);
checkDouble(".22250738585072014e-307", 2.2250738585072014e-308);
checkDouble("2.2250738585072014e-308", 2.2250738585072014e-308);
checkDouble("22.250738585072014e-309", 2.2250738585072014e-308);
checkDouble("222.50738585072014e-310", 2.2250738585072014e-308);
}
SECTION("VeryLong") {
checkDouble("0.00000000000000000000000000000001", 1e-32);
checkDouble("100000000000000000000000000000000.0", 1e+32);
checkDouble(
"100000000000000000000000000000000.00000000000000000000000000000",
1e+32);
}
SECTION("MantissaTooLongToFit") {
checkDouble("0.179769313486231571111111111111", 0.17976931348623157);
checkDouble("17976931348623157.11111111111111", 17976931348623157.0);
checkDouble("1797693.134862315711111111111111", 1797693.1348623157);
checkDouble("-0.179769313486231571111111111111", -0.17976931348623157);
checkDouble("-17976931348623157.11111111111111", -17976931348623157.0);
checkDouble("-1797693.134862315711111111111111", -1797693.1348623157);
}
SECTION("ExponentTooBig") {
checkDoubleInf("1e309", false);
checkDoubleInf("-1e309", true);
checkDoubleInf("1e65535", false);
checkDouble("1e-65535", 0.0);
}
SECTION("NaN") {
checkDoubleNaN("NaN");
checkDoubleNaN("nan");
}
}

View File

@ -6,28 +6,26 @@
#define ARDUINOJSON_ENABLE_NAN 1
#define ARDUINOJSON_ENABLE_INFINITY 1
#include <ArduinoJson/Numbers/parseFloat.hpp>
#include <ArduinoJson/Numbers/parseNumber.hpp>
#include <ArduinoJson/Variant/VariantImpl.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
template <typename T>
void checkFloat(const char* input, T expected) {
void checkFloat(const char* input, float expected) {
CAPTURE(input);
REQUIRE(parseFloat<T>(input) == Approx(expected));
REQUIRE(parseNumber<float>(input) == Approx(expected));
}
template <typename T>
void checkNaN(const char* input) {
void checkFloatNaN(const char* input) {
CAPTURE(input);
T result = parseFloat<T>(input);
float result = parseNumber<float>(input);
REQUIRE(result != result);
}
template <typename T>
void checkInf(const char* input, bool negative) {
void checkFloatInf(const char* input, bool negative) {
CAPTURE(input);
T x = parseFloat<T>(input);
float x = parseNumber<float>(input);
if (negative)
REQUIRE(x < 0);
else
@ -36,137 +34,69 @@ void checkInf(const char* input, bool negative) {
REQUIRE(x * 2 == x); // a property of infinity
}
TEST_CASE("parseFloat<float>()") {
TEST_CASE("parseNumber<float>()") {
SECTION("Float_Short_NoExponent") {
checkFloat<float>("3.14", 3.14f);
checkFloat<float>("-3.14", -3.14f);
checkFloat<float>("+3.14", +3.14f);
checkFloat("3.14", 3.14f);
checkFloat("-3.14", -3.14f);
checkFloat("+3.14", +3.14f);
}
SECTION("Short_NoDot") {
checkFloat<float>("1E+38", 1E+38f);
checkFloat<float>("-1E+38", -1E+38f);
checkFloat<float>("+1E-38", +1E-38f);
checkFloat<float>("+1e+38", +1e+38f);
checkFloat<float>("-1e-38", -1e-38f);
checkFloat("1E+38", 1E+38f);
checkFloat("-1E+38", -1E+38f);
checkFloat("+1E-38", +1E-38f);
checkFloat("+1e+38", +1e+38f);
checkFloat("-1e-38", -1e-38f);
}
SECTION("Max") {
checkFloat<float>("340.2823e+36", 3.402823e+38f);
checkFloat<float>("34.02823e+37", 3.402823e+38f);
checkFloat<float>("3.402823e+38", 3.402823e+38f);
checkFloat<float>("0.3402823e+39", 3.402823e+38f);
checkFloat<float>("0.03402823e+40", 3.402823e+38f);
checkFloat<float>("0.003402823e+41", 3.402823e+38f);
checkFloat("340.2823e+36", 3.402823e+38f);
checkFloat("34.02823e+37", 3.402823e+38f);
checkFloat("3.402823e+38", 3.402823e+38f);
checkFloat("0.3402823e+39", 3.402823e+38f);
checkFloat("0.03402823e+40", 3.402823e+38f);
checkFloat("0.003402823e+41", 3.402823e+38f);
}
SECTION("VeryLong") {
checkFloat<float>("0.00000000000000000000000000000001", 1e-32f);
checkFloat<float>("100000000000000000000000000000000.0", 1e+32f);
checkFloat<float>(
checkFloat("0.00000000000000000000000000000001", 1e-32f);
checkFloat("100000000000000000000000000000000.0", 1e+32f);
checkFloat(
"100000000000000000000000000000000.00000000000000000000000000000",
1e+32f);
}
SECTION("MantissaTooLongToFit") {
checkFloat<float>("0.340282346638528861111111111111", 0.34028234663852886f);
checkFloat<float>("34028234663852886.11111111111111", 34028234663852886.0f);
checkFloat<float>("34028234.66385288611111111111111", 34028234.663852886f);
checkFloat("0.340282346638528861111111111111", 0.34028234663852886f);
checkFloat("34028234663852886.11111111111111", 34028234663852886.0f);
checkFloat("34028234.66385288611111111111111", 34028234.663852886f);
checkFloat<float>("-0.340282346638528861111111111111",
-0.34028234663852886f);
checkFloat<float>("-34028234663852886.11111111111111",
-34028234663852886.0f);
checkFloat<float>("-34028234.66385288611111111111111",
-34028234.663852886f);
checkFloat("-0.340282346638528861111111111111", -0.34028234663852886f);
checkFloat("-34028234663852886.11111111111111", -34028234663852886.0f);
checkFloat("-34028234.66385288611111111111111", -34028234.663852886f);
}
SECTION("ExponentTooBig") {
checkInf<float>("1e39", false);
checkInf<float>("-1e39", true);
checkInf<float>("1e255", false);
checkFloat<float>("1e-255", 0.0f);
checkFloatInf("1e39", false);
checkFloatInf("-1e39", true);
checkFloatInf("1e255", false);
checkFloat("1e-255", 0.0f);
}
SECTION("NaN") {
checkNaN<float>("NaN");
checkNaN<float>("nan");
checkFloatNaN("NaN");
checkFloatNaN("nan");
}
SECTION("Infinity") {
checkInf<float>("Infinity", false);
checkInf<float>("+Infinity", false);
checkInf<float>("-Infinity", true);
checkInf<float>("inf", false);
checkInf<float>("+inf", false);
checkInf<float>("-inf", true);
checkFloatInf("Infinity", false);
checkFloatInf("+Infinity", false);
checkFloatInf("-Infinity", true);
checkFloatInf("inf", false);
checkFloatInf("+inf", false);
checkFloatInf("-inf", true);
checkInf<float>("1e300", false);
checkInf<float>("-1e300", true);
}
}
TEST_CASE("parseFloat<double>()") {
SECTION("Short_NoExponent") {
checkFloat<double>("3.14", 3.14);
checkFloat<double>("-3.14", -3.14);
checkFloat<double>("+3.14", +3.14);
}
SECTION("Short_NoDot") {
checkFloat<double>("1E+308", 1E+308);
checkFloat<double>("-1E+308", -1E+308);
checkFloat<double>("+1E-308", +1E-308);
checkFloat<double>("+1e+308", +1e+308);
checkFloat<double>("-1e-308", -1e-308);
}
SECTION("Max") {
checkFloat<double>(".017976931348623147e+310", 1.7976931348623147e+308);
checkFloat<double>(".17976931348623147e+309", 1.7976931348623147e+308);
checkFloat<double>("1.7976931348623147e+308", 1.7976931348623147e+308);
checkFloat<double>("17.976931348623147e+307", 1.7976931348623147e+308);
checkFloat<double>("179.76931348623147e+306", 1.7976931348623147e+308);
}
SECTION("Min") {
checkFloat<double>(".022250738585072014e-306", 2.2250738585072014e-308);
checkFloat<double>(".22250738585072014e-307", 2.2250738585072014e-308);
checkFloat<double>("2.2250738585072014e-308", 2.2250738585072014e-308);
checkFloat<double>("22.250738585072014e-309", 2.2250738585072014e-308);
checkFloat<double>("222.50738585072014e-310", 2.2250738585072014e-308);
}
SECTION("VeryLong") {
checkFloat<double>("0.00000000000000000000000000000001", 1e-32);
checkFloat<double>("100000000000000000000000000000000.0", 1e+32);
checkFloat<double>(
"100000000000000000000000000000000.00000000000000000000000000000",
1e+32);
}
SECTION("MantissaTooLongToFit") {
checkFloat<double>("0.179769313486231571111111111111", 0.17976931348623157);
checkFloat<double>("17976931348623157.11111111111111", 17976931348623157.0);
checkFloat<double>("1797693.134862315711111111111111", 1797693.1348623157);
checkFloat<double>("-0.179769313486231571111111111111",
-0.17976931348623157);
checkFloat<double>("-17976931348623157.11111111111111",
-17976931348623157.0);
checkFloat<double>("-1797693.134862315711111111111111",
-1797693.1348623157);
}
SECTION("ExponentTooBig") {
checkInf<double>("1e309", false);
checkInf<double>("-1e309", true);
checkInf<double>("1e65535", false);
checkFloat<double>("1e-65535", 0.0);
}
SECTION("NaN") {
checkNaN<double>("NaN");
checkNaN<double>("nan");
checkFloatInf("1e300", false);
checkFloatInf("-1e300", true);
}
}

View File

@ -3,7 +3,8 @@
// MIT License
#include <stdint.h>
#include <ArduinoJson/Numbers/parseInteger.hpp>
#include <ArduinoJson/Numbers/parseNumber.hpp>
#include <ArduinoJson/Variant/VariantImpl.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
@ -11,11 +12,11 @@ using namespace ARDUINOJSON_NAMESPACE;
template <typename T>
void checkInteger(const char* input, T expected) {
CAPTURE(input);
T actual = parseInteger<T>(input);
T actual = parseNumber<T>(input);
REQUIRE(expected == actual);
}
TEST_CASE("parseInteger<int8_t>()") {
TEST_CASE("parseNumber<int8_t>()") {
checkInteger<int8_t>("-128", -128);
checkInteger<int8_t>("127", 127);
checkInteger<int8_t>("+127", 127);
@ -25,7 +26,7 @@ TEST_CASE("parseInteger<int8_t>()") {
checkInteger<int8_t>("-129", 0); // overflow
}
TEST_CASE("parseInteger<int16_t>()") {
TEST_CASE("parseNumber<int16_t>()") {
checkInteger<int16_t>("-32768", -32768);
checkInteger<int16_t>("32767", 32767);
checkInteger<int16_t>("+32767", 32767);
@ -35,7 +36,7 @@ TEST_CASE("parseInteger<int16_t>()") {
checkInteger<int16_t>("32768", 0); // overflow
}
TEST_CASE("parseInteger<int32_t>()") {
TEST_CASE("parseNumber<int32_t>()") {
checkInteger<int32_t>("-2147483648", (-2147483647 - 1));
checkInteger<int32_t>("2147483647", 2147483647);
checkInteger<int32_t>("+2147483647", 2147483647);
@ -45,7 +46,7 @@ TEST_CASE("parseInteger<int32_t>()") {
checkInteger<int32_t>("2147483648", 0); // overflow
}
TEST_CASE("parseInteger<uint8_t>()") {
TEST_CASE("parseNumber<uint8_t>()") {
checkInteger<uint8_t>("0", 0);
checkInteger<uint8_t>("255", 255);
checkInteger<uint8_t>("+255", 255);
@ -55,7 +56,7 @@ TEST_CASE("parseInteger<uint8_t>()") {
checkInteger<uint8_t>("256", 0);
}
TEST_CASE("parseInteger<uint16_t>()") {
TEST_CASE("parseNumber<uint16_t>()") {
checkInteger<uint16_t>("0", 0);
checkInteger<uint16_t>("65535", 65535);
checkInteger<uint16_t>("+65535", 65535);

View File

@ -2,23 +2,38 @@
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson/Numbers/Integer.hpp>
#include <ArduinoJson/Numbers/parseNumber.hpp>
#include <ArduinoJson/Variant/VariantImpl.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("Test uint32_t overflow") {
ParsedNumber<float, uint32_t> first =
parseNumber<float, uint32_t>("4294967295");
ParsedNumber<float, uint32_t> second =
parseNumber<float, uint32_t>("4294967296");
TEST_CASE("Test unsigned integer overflow") {
VariantData first, second;
first.init();
second.init();
// Avoids MSVC warning C4127 (conditional expression is constant)
size_t integerSize = sizeof(Integer);
if (integerSize == 8) {
parseNumber("18446744073709551615", first);
parseNumber("18446744073709551616", second);
} else {
parseNumber("4294967295", first);
parseNumber("4294967296", second);
}
REQUIRE(first.type() == uint8_t(VALUE_IS_POSITIVE_INTEGER));
REQUIRE(second.type() == uint8_t(VALUE_IS_FLOAT));
}
TEST_CASE("Invalid value") {
ParsedNumber<float, uint32_t> result = parseNumber<float, uint32_t>("6a3");
VariantData result;
result.init();
parseNumber("6a3", result);
REQUIRE(result.type() == uint8_t(VALUE_IS_NULL));
}

View File

@ -7,11 +7,6 @@ add_executable(TextFormatterTests
writeString.cpp
)
target_link_libraries(TextFormatterTests
ArduinoJson
catch
)
set_target_properties(TextFormatterTests PROPERTIES UNITY_BUILD OFF)
add_test(TextFormatter TextFormatterTests)

View File

@ -7,7 +7,7 @@
"type": "git",
"url": "https://github.com/bblanchon/ArduinoJson.git"
},
"version": "6.15.1",
"version": "6.17.0",
"authors": {
"name": "Benoit Blanchon",
"url": "https://blog.benoitblanchon.fr"

View File

@ -1,5 +1,5 @@
name=ArduinoJson
version=6.15.1
version=6.17.0
author=Benoit Blanchon <blog.benoitblanchon.fr>
maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
sentence=A simple and efficient JSON library for embedded C++.

View File

@ -28,6 +28,7 @@
#include "ArduinoJson/Object/MemberProxy.hpp"
#include "ArduinoJson/Object/ObjectImpl.hpp"
#include "ArduinoJson/Variant/VariantAsImpl.hpp"
#include "ArduinoJson/Variant/VariantCompare.hpp"
#include "ArduinoJson/Variant/VariantImpl.hpp"
#include "ArduinoJson/Json/JsonDeserializer.hpp"

View File

@ -12,12 +12,13 @@ inline VariantData *arrayAdd(CollectionData *arr, MemoryPool *pool) {
return arr ? arr->addElement(pool) : 0;
}
template <typename Visitor>
inline void arrayAccept(const CollectionData *arr, Visitor &visitor) {
template <typename TVisitor>
inline typename TVisitor::result_type arrayAccept(const CollectionData *arr,
TVisitor &visitor) {
if (arr)
visitor.visitArray(*arr);
return visitor.visitArray(*arr);
else
visitor.visitNull();
return visitor.visitNull();
}
inline bool arrayEquals(const CollectionData *lhs, const CollectionData *rhs) {

View File

@ -27,9 +27,9 @@ class ArrayRefBase {
return VariantConstRef(reinterpret_cast<const VariantData*>(data));
}
template <typename Visitor>
FORCE_INLINE void accept(Visitor& visitor) const {
arrayAccept(_data, visitor);
template <typename TVisitor>
FORCE_INLINE typename TVisitor::result_type accept(TVisitor& visitor) const {
return arrayAccept(_data, visitor);
}
FORCE_INLINE bool isNull() const {

View File

@ -5,7 +5,8 @@
#pragma once
#include <ArduinoJson/Configuration.hpp>
#include <ArduinoJson/Operators/VariantOperators.hpp>
#include <ArduinoJson/Variant/VariantOperators.hpp>
#include <ArduinoJson/Variant/VariantShortcuts.hpp>
#include <ArduinoJson/Variant/VariantTo.hpp>
#ifdef _MSC_VER
@ -17,6 +18,7 @@ namespace ARDUINOJSON_NAMESPACE {
template <typename TArray>
class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
public VariantShortcuts<ElementProxy<TArray> >,
public Visitable {
typedef ElementProxy<TArray> this_type;
@ -51,14 +53,6 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
return *this;
}
FORCE_INLINE bool operator==(VariantConstRef rhs) const {
return static_cast<VariantConstRef>(getUpstreamElement()) == rhs;
}
FORCE_INLINE bool operator!=(VariantConstRef rhs) const {
return static_cast<VariantConstRef>(getUpstreamElement()) != rhs;
}
FORCE_INLINE void clear() const {
getUpstreamElement().clear();
}
@ -104,8 +98,8 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
return getOrAddUpstreamElement().set(value);
}
template <typename Visitor>
void accept(Visitor& visitor) const {
template <typename TVisitor>
typename TVisitor::result_type accept(TVisitor& visitor) const {
return getUpstreamElement().accept(visitor);
}
@ -141,6 +135,10 @@ class ElementProxy : public VariantOperators<ElementProxy<TArray> >,
return getOrAddUpstreamElement().getElement(index);
}
VariantRef getOrAddElement(size_t index) const {
return getOrAddUpstreamElement().getOrAddElement(index);
}
FORCE_INLINE void remove(size_t index) const {
getUpstreamElement().remove(index);
}

View File

@ -5,18 +5,31 @@
#pragma once
#include <ArduinoJson/Array/ArrayRef.hpp>
#include <ArduinoJson/Document/JsonDocument.hpp>
namespace ARDUINOJSON_NAMESPACE {
// Copy a 1D array to a JsonArray
template <typename T, size_t N>
inline bool copyArray(T (&src)[N], ArrayRef dst) {
template <typename T, size_t N, typename TDestination>
inline typename enable_if<!is_array<T>::value &&
!is_base_of<JsonDocument, TDestination>::value,
bool>::type
copyArray(T (&src)[N], const TDestination& dst) {
return copyArray(src, N, dst);
}
// Copy a 1D array to a JsonDocument
template <typename T, size_t N>
inline bool copyArray(T (&src)[N], JsonDocument& dst) {
return copyArray(src, dst.to<ArrayRef>());
}
// Copy a 1D array to a JsonArray
template <typename T>
inline bool copyArray(T* src, size_t len, ArrayRef dst) {
template <typename T, typename TDestination>
inline typename enable_if<!is_array<T>::value &&
!is_base_of<JsonDocument, TDestination>::value,
bool>::type
copyArray(T* src, size_t len, const TDestination& dst) {
bool ok = true;
for (size_t i = 0; i < len; i++) {
ok &= dst.add(src[i]);
@ -24,9 +37,17 @@ inline bool copyArray(T* src, size_t len, ArrayRef dst) {
return ok;
}
// Copy a 1D array to a JsonDocument
template <typename T>
inline bool copyArray(T* src, size_t len, JsonDocument& dst) {
return copyArray(src, len, dst.to<ArrayRef>());
}
// Copy a 2D array to a JsonArray
template <typename T, size_t N1, size_t N2>
inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) {
template <typename T, size_t N1, size_t N2, typename TDestination>
inline typename enable_if<!is_base_of<JsonDocument, TDestination>::value,
bool>::type
copyArray(T (&src)[N1][N2], const TDestination& dst) {
bool ok = true;
for (size_t i = 0; i < N1; i++) {
ArrayRef nestedArray = dst.createNestedArray();
@ -37,30 +58,114 @@ inline bool copyArray(T (&src)[N1][N2], ArrayRef dst) {
return ok;
}
// Copy a 2D array to a JsonDocument
template <typename T, size_t N1, size_t N2>
inline bool copyArray(T (&src)[N1][N2], JsonDocument& dst) {
return copyArray(src, dst.to<ArrayRef>());
}
template <typename T>
class ArrayCopier1D : public Visitor<size_t> {
public:
ArrayCopier1D(T* destination, size_t capacity)
: _destination(destination), _capacity(capacity) {}
size_t visitArray(const CollectionData& array) {
size_t size = 0;
VariantSlot* slot = array.head();
while (slot != 0 && size < _capacity) {
_destination[size++] = variantAs<T>(slot->data());
slot = slot->next();
}
return size;
}
size_t visitObject(const CollectionData&) {
return 0;
}
size_t visitFloat(Float) {
return 0;
}
size_t visitString(const char*) {
return 0;
}
size_t visitRawJson(const char*, size_t) {
return 0;
}
size_t visitNegativeInteger(UInt) {
return 0;
}
size_t visitPositiveInteger(UInt) {
return 0;
}
size_t visitBoolean(bool) {
return 0;
}
size_t visitNull() {
return 0;
}
private:
T* _destination;
size_t _capacity;
};
template <typename T, size_t N1, size_t N2>
class ArrayCopier2D : public Visitor<void> {
public:
ArrayCopier2D(T (*destination)[N1][N2]) : _destination(destination) {}
void visitArray(const CollectionData& array) {
VariantSlot* slot = array.head();
size_t n = 0;
while (slot != 0 && n < N1) {
ArrayCopier1D<T> copier((*_destination)[n++], N2);
variantAccept(slot->data(), copier);
slot = slot->next();
}
}
void visitObject(const CollectionData&) {}
void visitFloat(Float) {}
void visitString(const char*) {}
void visitRawJson(const char*, size_t) {}
void visitNegativeInteger(UInt) {}
void visitPositiveInteger(UInt) {}
void visitBoolean(bool) {}
void visitNull() {}
private:
T (*_destination)[N1][N2];
size_t _capacity1, _capacity2;
};
// Copy a JsonArray to a 1D array
template <typename T, size_t N>
inline size_t copyArray(ArrayConstRef src, T (&dst)[N]) {
template <typename TSource, typename T, size_t N>
inline typename enable_if<!is_array<T>::value, size_t>::type copyArray(
const TSource& src, T (&dst)[N]) {
return copyArray(src, dst, N);
}
// Copy a JsonArray to a 1D array
template <typename T>
inline size_t copyArray(ArrayConstRef src, T* dst, size_t len) {
size_t i = 0;
for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < len;
++it)
dst[i++] = *it;
return i;
template <typename TSource, typename T>
inline size_t copyArray(const TSource& src, T* dst, size_t len) {
ArrayCopier1D<T> copier(dst, len);
return src.accept(copier);
}
// Copy a JsonArray to a 2D array
template <typename T, size_t N1, size_t N2>
inline void copyArray(ArrayConstRef src, T (&dst)[N1][N2]) {
size_t i = 0;
for (ArrayConstRef::iterator it = src.begin(); it != src.end() && i < N1;
++it) {
copyArray(it->as<ArrayConstRef>(), dst[i++]);
}
template <typename TSource, typename T, size_t N1, size_t N2>
inline void copyArray(const TSource& src, T (&dst)[N1][N2]) {
ArrayCopier2D<T, N1, N2> copier(&dst);
src.accept(copier);
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -9,6 +9,10 @@
namespace ARDUINOJSON_NAMESPACE {
inline bool variantEquals(const VariantData* a, const VariantData* b) {
return variantCompare(a, b) == COMPARE_RESULT_EQUAL;
}
inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) {
VariantSlot* slot = pool->allocVariant();
if (!slot)

View File

@ -4,12 +4,6 @@
#pragma once
#if defined(_MSC_VER)
#define ARDUINOJSON_HAS_INT64 1
#else
#define ARDUINOJSON_HAS_INT64 0
#endif
#if __cplusplus >= 201103L
#define ARDUINOJSON_HAS_LONG_LONG 1
#define ARDUINOJSON_HAS_NULLPTR 1
@ -20,6 +14,12 @@
#define ARDUINOJSON_HAS_RVALUE_REFERENCES 0
#endif
#if defined(_MSC_VER) && !ARDUINOJSON_HAS_LONG_LONG
#define ARDUINOJSON_HAS_INT64 1
#else
#define ARDUINOJSON_HAS_INT64 0
#endif
// Small or big machine?
#ifndef ARDUINOJSON_EMBEDDED_MODE
#if defined(ARDUINO) /* Arduino*/ \
@ -164,7 +164,7 @@
// Convert unicode escape sequence (\u0123) to UTF-8
#ifndef ARDUINOJSON_DECODE_UNICODE
#define ARDUINOJSON_DECODE_UNICODE 0
#define ARDUINOJSON_DECODE_UNICODE 1
#endif
// Ignore comments in input
@ -215,6 +215,10 @@
#define ARDUINOJSON_TAB " "
#endif
#ifndef ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
#define ARDUINOJSON_ENABLE_STRING_DEDUPLICATION 1
#endif
#ifndef ARDUINOJSON_STRING_BUFFER_SIZE
#define ARDUINOJSON_STRING_BUFFER_SIZE 32
#endif
@ -226,3 +230,8 @@
#define ARDUINOJSON_DEBUG 0
#endif
#endif
#if ARDUINOJSON_HAS_NULLPTR && defined(nullptr)
#error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr
// See https://github.com/bblanchon/ArduinoJson/issues/1355
#endif

View File

@ -5,6 +5,8 @@
#pragma once
#include <ArduinoJson/Namespace.hpp>
#include <ArduinoJson/Polyfills/preprocessor.hpp>
#include <ArduinoJson/Polyfills/static_array.hpp>
#if ARDUINOJSON_ENABLE_STD_STREAM
#include <ostream>
@ -20,6 +22,7 @@ class DeserializationError {
public:
enum Code {
Ok,
EmptyInput,
IncompleteInput,
InvalidInput,
NoMemory,
@ -77,24 +80,31 @@ class DeserializationError {
}
const char* c_str() const {
switch (_code) {
case Ok:
return "Ok";
case TooDeep:
return "TooDeep";
case NoMemory:
return "NoMemory";
case InvalidInput:
return "InvalidInput";
case IncompleteInput:
return "IncompleteInput";
case NotSupported:
return "NotSupported";
default:
return "???";
}
static const char* messages[] = {
"Ok", "EmptyInput", "IncompleteInput", "InvalidInput",
"NoMemory", "NotSupported", "TooDeep"};
ARDUINOJSON_ASSERT(static_cast<size_t>(_code) <
sizeof(messages) / sizeof(messages[0]));
return messages[_code];
}
#if ARDUINOJSON_ENABLE_PROGMEM
const __FlashStringHelper* f_str() const {
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s0, "Ok");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s1, "EmptyInput");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s2, "IncompleteInput");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s3, "InvalidInput");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s4, "NoMemory");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s5, "NotSupported");
ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s6, "TooDeep");
ARDUINOJSON_DEFINE_STATIC_ARRAY(
const char*, messages,
ARDUINOJSON_EXPAND7({s0, s1, s2, s3, s4, s5, s6}));
return ARDUINOJSON_READ_STATIC_ARRAY(const __FlashStringHelper*, messages,
_code);
}
#endif
private:
Code _code;
};

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