Compare commits

...

102 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
3ea5eb3f3a Set version to 6.15.1 2020-04-08 18:23:27 +02:00
ec43bf4fe9 Simplified the implementation of implicit casts 2020-04-08 18:18:18 +02:00
2097ffaabf Updated README 2020-04-08 11:16:22 +02:00
67e78f0751 Disabled alignment on AVR (fixes #1231) 2020-04-08 11:16:22 +02:00
1791dccbf2 Added CMake "install" target (closes #1209) 2020-04-07 21:43:10 +02:00
40d1cfe7af Fixed "pointless integer comparison" warning on IAR (issue #1233) 2020-04-06 18:20:12 +02:00
4627f851ca Fixed "statement is unreachable" warning on IAR (issue #1233) 2020-04-06 18:19:32 +02:00
fc9e609ab5 Fixed "maybe-uninitialized" warning (fixes #1217) 2020-03-25 08:37:57 +01:00
8b3d861a9d Updated README 2020-03-23 13:39:58 +01:00
9ef864b27c Fixed markup in changelog 2020-03-23 11:03:30 +01:00
275b80a462 Updated library description 2020-03-23 11:02:58 +01:00
763be4f266 Set version to 6.15.0 2020-03-22 21:02:38 +01:00
5b812522fa Enabled debug mode when PlatformIO builds in debug 2020-03-20 20:58:08 +01:00
9cb0ddb5e7 Removed the copy-constructor of JsonDocument (issue #1189) 2020-03-05 13:46:45 +01:00
735bea1f47 Added StaticJsonDocument::garbageCollect() 2020-03-02 14:50:16 +01:00
0853b04589 Changed BasicJsonDocument's copy-constructor to copy the capacity 2020-03-02 12:39:29 +01:00
c1b3705df1 Added BasicJsonDocument::garbageCollect() (issue #1195) 2020-03-01 18:01:55 +01:00
2540b4e01b Added move-constructor and move-assignment to BasicJsonDocument 2020-03-01 17:24:29 +01:00
2641697e0b Fixed incorrect string comparison on some platforms (fixes #1198) 2020-02-27 11:44:09 +01:00
2996503b27 Fixed enums serialized as booleans (fixes #1197) 2020-02-26 16:16:20 +01:00
0214c9bcad Improved coverage of JsonObject 2020-02-23 13:03:14 +01:00
a471aed6db Improved coverage of JsonObject 2020-02-22 14:17:10 +01:00
af0edecddb Fixed MemberProxy::set(char[]) not duplicating the string (fixes #1191) 2020-02-22 12:06:39 +01:00
300323cfd7 Improved coverage of JsonArray 2020-02-20 09:18:10 +01:00
d8724e0a0b Changed the array subscript to automatically add missing elements 2020-02-20 08:59:25 +01:00
0001dabfd1 Improved coverage of MemoryPool 2020-02-19 17:27:04 +01:00
ad78001241 Removed useless null checks 2020-02-19 17:22:23 +01:00
5a837a591e Improved coverage of MsgPackDeserializer 2020-02-19 17:14:40 +01:00
fd79d23910 Updated copyright year 2020-02-19 16:07:34 +01:00
1902c0ec93 Improved coverage of JsonDeserializer 2020-02-19 10:31:56 +01:00
85499be855 Added tests for FlashStringAdapter 2020-02-16 17:56:47 +01:00
e6ddfc7afb Fixed static JsonString not being saved by reference 2020-02-16 17:34:04 +01:00
58298ec5ba Extracted storage_policy to improve coverage in string adapters 2020-02-16 17:34:04 +01:00
0814fc185f Added a line-break after each "if" to get more accurate coverage report 2020-02-16 15:05:23 +01:00
8f8c82d400 Reduced code size 2020-02-13 17:05:08 +01:00
372b7d3d9d Exposed JsonPairConst 2020-02-13 16:58:23 +01:00
fbffadb2cf Moved nesting decrement logic to class NestingLimit 2020-02-13 16:54:18 +01:00
6e52f242b2 Extracted the class Latch
also fixed a buffer overrun and reduced the code size
2020-02-13 14:53:54 +01:00
d2a67f362f Fixed "deprecated-copy" warning on GCC 9 (fixes #1184) 2020-02-13 11:07:40 +01:00
66b12da4e7 Added DeserializationOption::Filter (closes #959) 2020-02-12 17:22:42 +01:00
216 changed files with 8571 additions and 2992 deletions

View File

@ -3,3 +3,6 @@
BasedOnStyle: Google
Standard: Cpp03
AllowShortFunctionsOnASingleLine: Empty
# Always break after if to get accurate coverage
AllowShortIfStatementsOnASingleLine: false

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,12 +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 CXXFLAGS="-fsanitize=address"
- addons:
apt:
packages: ['g++-arm-linux-gnueabihf']
@ -56,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']
@ -71,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']
@ -102,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
@ -114,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,113 @@
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)
-------
* Fixed "maybe-uninitialized" warning (issue #1217)
* Fixed "statement is unreachable" warning on IAR (issue #1233)
* Fixed "pointless integer comparison" warning on IAR (issue #1233)
* Added CMake "install" target (issue #1209)
* Disabled alignment on AVR (issue #1231)
v6.15.0 (2020-03-22)
-------
* Added `DeserializationOption::Filter` (issue #959)
* Added example `JsonFilterExample.ino`
* Changed the array subscript operator to automatically add missing elements
* Fixed "deprecated-copy" warning on GCC 9 (fixes #1184)
* Fixed `MemberProxy::set(char[])` not duplicating the string (issue #1191)
* Fixed enums serialized as booleans (issue #1197)
* Fixed incorrect string comparison on some platforms (issue #1198)
* Added move-constructor and move-assignment to `BasicJsonDocument`
* Added `BasicJsonDocument::garbageCollect()` (issue #1195)
* Added `StaticJsonDocument::garbageCollect()`
* Changed copy-constructor of `BasicJsonDocument` to preserve the capacity of the source.
* Removed copy-constructor of `JsonDocument` (issue #1189)
> ### BREAKING CHANGES
>
> #### Copy-constructor of `BasicJsonDocument`
>
> In previous versions, the copy constructor of `BasicJsonDocument` looked at the source's `memoryUsage()` to choose its capacity.
> Now, the copy constructor of `BasicJsonDocument` uses the same capacity as the source.
>
> Example:
>
> ```c++
> DynamicJsonDocument doc1(64);
> doc1.set(String("example"));
>
> DynamicJsonDocument doc2 = doc1;
> Serial.print(doc2.capacity()); // 8 with ArduinoJson 6.14
> // 64 with ArduinoJson 6.15
> ```
>
> I made this change to get consistent results between copy-constructor and move-constructor, and whether RVO applies or not.
>
> If you use the copy-constructor to optimize your documents, you can use `garbageCollect()` or `shrinkToFit()` instead.
>
> #### Copy-constructor of `JsonDocument`
>
> In previous versions, it was possible to create a function that take a `JsonDocument` by value.
>
> ```c++
> void myFunction(JsonDocument doc) {}
> ```
>
> This function gives the wrong clues because it doesn't receive a copy of the `JsonDocument`, only a sliced version.
> It worked because the copy constructor copied the internal pointers, but it was an accident.
>
> From now, if you need to pass a `JsonDocument` to a function, you must use a reference:
>
> ```c++
> void myFunction(JsonDocument& doc) {}
> ```
v6.14.1 (2020-01-27)
-------

View File

@ -3,19 +3,17 @@
# MIT License
cmake_minimum_required(VERSION 3.0)
project(ArduinoJson)
enable_testing()
project(ArduinoJson VERSION 6.17.0)
add_definitions(-DARDUINOJSON_DEBUG)
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
add_compile_options(-g -O0)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)
endif()
if(${COVERAGE})
set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage")
endif()
add_subdirectory(src)
include_directories(${CMAKE_CURRENT_LIST_DIR}/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

@ -1,7 +1,7 @@
The MIT License (MIT)
---------------------
Copyright © 2014-2019 Benoit BLANCHON
Copyright © 2014-2020 Benoit BLANCHON
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -2,7 +2,7 @@
---
[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoJson.svg?version=6.14.1)](https://www.ardu-badge.com/ArduinoJson/6.14.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)
@ -13,28 +13,31 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
## Features
* [JSON deserialization](https://arduinojson.org/v6/api/json/deserializejson/)
* [Optionally decodes UTF-16 escape sequences to UTF-8](https://arduinojson.org/v6/api/config/decode_unicode/)
* [Optionally stores links to the input buffer (zero-copy)](https://arduinojson.org/v6/api/json/deserializejson/)
* [Optionally supports comments in the input](https://arduinojson.org/v6/api/config/enable_comments/)
* [JSON deserialization](https://arduinojson.org/v6/api/json/deserializejson/?utm_source=github&utm_medium=readme)
* [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/?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/)
* [Can write to a buffer or a stream](https://arduinojson.org/v6/api/json/serializejson/)
* [Optionally indents the document (prettified JSON)](https://arduinojson.org/v6/api/json/serializejsonpretty/)
* [MessagePack serialization](https://arduinojson.org/v6/api/msgpack/serializemsgpack/)
* [MessagePack deserialization](https://arduinojson.org/v6/api/msgpack/deserializemsgpack/)
* [JSON serialization](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme)
* [Can write to a buffer or a stream](https://arduinojson.org/v6/api/json/serializejson/?utm_source=github&utm_medium=readme)
* [Optionally indents the document (prettified JSON)](https://arduinojson.org/v6/api/json/serializejsonpretty/?utm_source=github&utm_medium=readme)
* [MessagePack serialization](https://arduinojson.org/v6/api/msgpack/serializemsgpack/?utm_source=github&utm_medium=readme)
* [MessagePack deserialization](https://arduinojson.org/v6/api/msgpack/deserializemsgpack/?utm_source=github&utm_medium=readme)
* Efficient
* [Twice smaller than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Almost 10% faster than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Consumes roughly 10% less RAM than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/)
* [Fixed memory allocation, no heap fragmentation](https://arduinojson.org/v6/api/jsondocument/)
* [Optionally works without heap memory (zero malloc)](https://arduinojson.org/v6/api/staticjsondocument/)
* [Twice smaller than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme)
* [Almost 10% faster than the "official" Arduino_JSON library](https://arduinojson.org/2019/11/19/arduinojson-vs-arduino_json/?utm_source=github&utm_medium=readme)
* [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/)
* 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/)
* Supports Arduino's `Stream` and [STL's `std::istream`/`std::ostream`](https://arduinojson.org/v6/api/config/enable_std_stream/)
* [Supports Flash strings](https://arduinojson.org/v6/api/config/enable_progmem/)
* [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/?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
@ -60,13 +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
* [`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/?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
@ -75,11 +80,11 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
* [Clang 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 5.0, 6.0, 7, 8](https://travis-ci.org/bblanchon/ArduinoJson)
* [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
* Well documented
* [Tutorials](https://arduinojson.org/v6/doc/deserialization/)
* [Examples](https://arduinojson.org/v6/example/)
* [How-tos](https://arduinojson.org/v6/example/)
* [FAQ](https://arduinojson.org/v6/faq/)
* [Book](https://arduinojson.org/book/)
* [Tutorials](https://arduinojson.org/v6/doc/deserialization/?utm_source=github&utm_medium=readme)
* [Examples](https://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme)
* [How-tos](https://arduinojson.org/v6/example/?utm_source=github&utm_medium=readme)
* [FAQ](https://arduinojson.org/v6/faq/?utm_source=github&utm_medium=readme)
* [Book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme)
* Vibrant user community
* Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories) and [PlatformIO](https://platformio.org/lib/search)
* [Used in hundreds of projects](https://www.hackster.io/search?i=projects&q=arduinojson)
@ -114,10 +119,8 @@ DynamicJsonDocument doc(1024);
doc["sensor"] = "gps";
doc["time"] = 1351824120;
JsonArray data = doc.createNestedArray("data");
data.add(48.756080);
data.add(2.302038);
doc["data"][0] = 48.756080;
doc["data"][1] = 2.302038;
serializeJson(doc, Serial);
// This prints:
@ -130,5 +133,5 @@ See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/?utm_
Do you like this library? Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)!
What? You don't like it but you *love* it?
We don't take donations anymore, but [we sell a book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme), so you can help and learn at the same time!
What? You don't like it but you *love* it?
We don't take donations anymore, but [we sell a book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme), so you can help and learn at the same time.

View File

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

View File

@ -0,0 +1,63 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
//
// This example shows how to use DeserializationOpion::Filter
//
// https://arduinojson.org/v6/example/filter/
#include <ArduinoJson.h>
void setup() {
// Initialize serial port
Serial.begin(9600);
while (!Serial) continue;
// The huge input: an extract from OpenWeatherMap response
const __FlashStringHelper* input_json = F(
"{\"cod\":\"200\",\"message\":0,\"list\":[{\"dt\":1581498000,\"main\":{"
"\"temp\":3.23,\"feels_like\":-3.63,\"temp_min\":3.23,\"temp_max\":4.62,"
"\"pressure\":1014,\"sea_level\":1014,\"grnd_level\":1010,\"humidity\":"
"58,\"temp_kf\":-1.39},\"weather\":[{\"id\":800,\"main\":\"Clear\","
"\"description\":\"clear "
"sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":0},\"wind\":{\"speed\":6."
"19,\"deg\":266},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
"09:00:00\"},{\"dt\":1581508800,\"main\":{\"temp\":6.09,\"feels_like\":-"
"1.07,\"temp_min\":6.09,\"temp_max\":7.13,\"pressure\":1015,\"sea_"
"level\":1015,\"grnd_level\":1011,\"humidity\":48,\"temp_kf\":-1.04},"
"\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear "
"sky\",\"icon\":\"01d\"}],\"clouds\":{\"all\":9},\"wind\":{\"speed\":6."
"64,\"deg\":268},\"sys\":{\"pod\":\"d\"},\"dt_txt\":\"2020-02-12 "
"12:00:00\"}],\"city\":{\"id\":2643743,\"name\":\"London\",\"coord\":{"
"\"lat\":51.5085,\"lon\":-0.1257},\"country\":\"GB\",\"population\":"
"1000000,\"timezone\":0,\"sunrise\":1581492085,\"sunset\":1581527294}}");
// The filter: it contains "true" for each value we want to keep
StaticJsonDocument<200> filter;
filter["list"][0]["dt"] = true;
filter["list"][0]["main"]["temp"] = true;
// Deserialize the document
StaticJsonDocument<400> doc;
deserializeJson(doc, input_json, DeserializationOption::Filter(filter));
// Print the result
serializeJsonPretty(doc, Serial);
}
void loop() {
// not used in this example
}
// See also
// --------
//
// https://arduinojson.org/ contains the documentation for all the functions
// used above. It also includes an FAQ that will help you solve any
// deserialization problem.
//
// The book "Mastering ArduinoJson" contains a tutorial on deserialization.
// It begins with a simple example, like the one above, and then adds more
// features like deserializing directly from a file or an HTTP request.
// Learn more at https://arduinojson.org/book/
// Use the coupon code TWENTY for a 20% discount ❤❤❤❤❤

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

View File

@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@")

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,12 +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_reproducer
ArduinoJson
)
add_executable(json_fuzzer
add_executable(json_reproducer
json_fuzzer.cpp
fuzzer_main.cpp
reproducer.cpp
)
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

@ -1,6 +1,6 @@
# CAUTION: this file is invoked by https://github.com/google/oss-fuzz
CXXFLAGS += -I../../src -DARDUINOJSON_DEBUG
CXXFLAGS += -I../../src -DARDUINOJSON_DEBUG=1
all: \
$(OUT)/json_fuzzer \

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,7 +9,7 @@ add_executable(ElementProxyTests
remove.cpp
set.cpp
size.cpp
subscript.cpp
)
target_link_libraries(ElementProxyTests catch)
add_test(ElementProxy ElementProxyTests)

View File

@ -23,4 +23,12 @@ TEST_CASE("ElementProxy::add()") {
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
}
SECTION("set(char[])") {
char s[] = "world";
ep.add(s);
strcpy(s, "!!!!!");
REQUIRE(doc.as<std::string>() == "[[\"world\"]]");
}
}

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

@ -9,7 +9,6 @@ using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("ElementProxy::set()") {
DynamicJsonDocument doc(4096);
doc.addElement();
ElementProxy<JsonDocument&> ep = doc[0];
SECTION("set(int)") {
@ -23,4 +22,12 @@ TEST_CASE("ElementProxy::set()") {
REQUIRE(doc.as<std::string>() == "[\"world\"]");
}
SECTION("set(char[])") {
char s[] = "world";
ep.set(s);
strcpy(s, "!!!!!");
REQUIRE(doc.as<std::string>() == "[\"world\"]");
}
}

View File

@ -0,0 +1,25 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
TEST_CASE("ElementProxy::operator[]") {
DynamicJsonDocument doc(4096);
ElementProxy<JsonDocument&> ep = doc[1];
SECTION("set member") {
ep["world"] = 42;
REQUIRE(doc.as<std::string>() == "[null,{\"world\":42}]");
}
SECTION("set element") {
ep[2] = 42;
REQUIRE(doc.as<std::string>() == "[null,[null,null,42]]");
}
}

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,13 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson.h>
// a function should not be able to get a JsonDocument by value
void f(JsonDocument) {}
int main() {
DynamicJsonDocument doc(1024);
f(doc);
}

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

@ -0,0 +1,42 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <stdint.h> // uint8_t
#include <string.h> // strcmp, strlen...
#define PROGMEM
class __FlashStringHelper;
inline const void* convertPtrToFlash(const void* s) {
return reinterpret_cast<const char*>(s) + 42;
}
inline const void* convertFlashToPtr(const void* s) {
return reinterpret_cast<const char*>(s) - 42;
}
#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

@ -6,6 +6,7 @@ add_executable(IntegrationTests
gbathree.cpp
issue772.cpp
round_trip.cpp
openweathermap.cpp
)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
@ -15,5 +16,4 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
)
endif()
target_link_libraries(IntegrationTests catch)
add_test(IntegrationTests IntegrationTests)

File diff suppressed because one or more lines are too long

View File

@ -19,5 +19,4 @@ add_executable(JsonArrayTests
undefined.cpp
)
target_link_libraries(JsonArrayTests 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

@ -31,7 +31,7 @@ TEST_CASE("JsonArray::operator==()") {
REQUIRE_FALSE(array1c == array2c);
}
SECTION("should return false when RKS has more elements") {
SECTION("should return false when RHS has more elements") {
array1.add(1);
array2.add(1);
array2.add(2);
@ -47,4 +47,23 @@ TEST_CASE("JsonArray::operator==()") {
REQUIRE(array1 == array2);
REQUIRE(array1c == array2c);
}
SECTION("should return false when RHS is null") {
JsonArray null;
REQUIRE_FALSE(array1 == null);
}
SECTION("should return false when LHS is null") {
JsonArray null;
REQUIRE_FALSE(null == array1);
}
SECTION("should return true when both are null") {
JsonArray null1;
JsonArray null2;
REQUIRE(null1 == null2);
}
}

View File

@ -28,9 +28,25 @@ static void run_iterator_test() {
}
TEST_CASE("JsonArray::begin()/end()") {
run_iterator_test<JsonArray>();
SECTION("Non null JsonArray") {
run_iterator_test<JsonArray>();
}
SECTION("Null JsonArray") {
JsonArray array;
REQUIRE(array.begin() == array.end());
}
}
TEST_CASE("JsonArrayConst::begin()/end()") {
run_iterator_test<JsonArrayConst>();
SECTION("Non null JsonArrayConst") {
run_iterator_test<JsonArrayConst>();
}
SECTION("Null JsonArrayConst") {
JsonArrayConst array;
REQUIRE(array.begin() == array.end());
}
}

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

@ -9,7 +9,20 @@
TEST_CASE("JsonArray::operator[]") {
DynamicJsonDocument doc(4096);
JsonArray array = doc.to<JsonArray>();
array.add(0);
SECTION("Pad with null") {
array[2] = 2;
array[5] = 5;
REQUIRE(array.size() == 6);
REQUIRE(array[0].isNull() == true);
REQUIRE(array[1].isNull() == true);
REQUIRE(array[2].isNull() == false);
REQUIRE(array[3].isNull() == true);
REQUIRE(array[4].isNull() == true);
REQUIRE(array[5].isNull() == false);
REQUIRE(array[2] == 2);
REQUIRE(array[5] == 5);
}
SECTION("int") {
array[0] = 123;
@ -106,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

@ -6,18 +6,18 @@ add_executable(JsonDeserializerTests
array.cpp
array_static.cpp
DeserializationError.cpp
filter.cpp
incomplete_input.cpp
input_types.cpp
number.cpp
invalid_input.cpp
misc.cpp
nestingLimit.cpp
number.cpp
object.cpp
object_static.cpp
string.cpp
)
target_link_libraries(JsonDeserializerTests 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

@ -0,0 +1,750 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2020
// MIT License
#define ARDUINOJSON_ENABLE_COMMENTS 1
#include <ArduinoJson.h>
#include <catch.hpp>
#include <sstream>
#include <string>
TEST_CASE("Filtering") {
struct TestCase {
const char* input;
const char* filter;
uint8_t nestingLimit;
DeserializationError error;
const char* output;
size_t memoryUsage;
};
// clang-format off
TestCase testCases[] = {
{
"{\"hello\":\"world\"}", // 1. input
"null", // 2. filter
10, // 3. nestingLimit
DeserializationError::Ok, // 4. error
"null", // 5. output
0 // 6. memoryUsage
},
{
"{\"hello\":\"world\"}",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
"{\"abcdefg\":\"hijklmn\"}",
"true",
10,
DeserializationError::Ok,
"{\"abcdefg\":\"hijklmn\"}",
JSON_OBJECT_SIZE(1) + 16
},
{
"{\"hello\":\"world\"}",
"{}",
10,
DeserializationError::Ok,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// Input in an object, but filter wants an array
"{\"hello\":\"world\"}",
"[]",
10,
DeserializationError::Ok,
"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\"]",
"{}",
10,
DeserializationError::Ok,
"null",
0
},
{
// Input is a bool, but filter wants an object
"true",
"{}",
10,
DeserializationError::Ok,
"null",
0
},
{
// Input is a string, but filter wants an object
"\"hello\"",
"{}",
10,
DeserializationError::Ok,
"null",
0
},
{
// skip an integer
"{\"an_integer\":666,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// skip a float
"{\"a_float\":12.34e-6,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip a boolean
"{\"a_bool\":false,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip a double-quoted string
"{\"a_double_quoted_string\":\"hello\",example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip a single-quoted string
"{\"a_single_quoted_string\":'hello',example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an empty array
"{\"an_empty_array\":[],example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an empty array with spaces in it
"{\"an_empty_array\":[\t],example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an array
"{\"an_array\":[1,2,3],example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an array with spaces in it
"{\"an_array\": [ 1 , 2 , 3 ] ,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an empty object
"{\"an_empty_object\":{},example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an empty object with spaces in it
"{\"an_empty_object\":{ },example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// can skip an object
"{\"an_object\":{a:1,'b':2,\"c\":3},example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
// skip an object with spaces in it
"{\"an_object\" : { a : 1 , 'b' : 2 , \"c\" : 3 } ,example:42}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{\"example\":42}",
JSON_OBJECT_SIZE(1) + 8
},
{
"{\"an_integer\": 0,\"example\":{\"type\":\"int\",\"outcome\":42}}",
"{\"example\":{\"outcome\":true}}",
10,
DeserializationError::Ok,
"{\"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]",
"[true, false]",
10,
DeserializationError::Ok,
"[1,2,3]",
JSON_ARRAY_SIZE(3)
},
{
// only the first element of array counts
"[1,2,3]",
"[false, true]",
10,
DeserializationError::Ok,
"[]",
JSON_ARRAY_SIZE(0)
},
{
// filter members of object in array
"[{\"example\":1,\"ignore\":2},{\"example\":3,\"ignore\":4}]",
"[{\"example\":true}]",
10,
DeserializationError::Ok,
"[{\"example\":1},{\"example\":3}]",
JSON_ARRAY_SIZE(2) + 2 * JSON_OBJECT_SIZE(1) + 8
},
{
"[',2,3]",
"[false,true]",
10,
DeserializationError::IncompleteInput,
"[]",
JSON_ARRAY_SIZE(0)
},
{
"[\",2,3]",
"[false,true]",
10,
DeserializationError::IncompleteInput,
"[]",
JSON_ARRAY_SIZE(0)
},
{
// detect errors in skipped value
"[!,2,\\]",
"[false]",
10,
DeserializationError::InvalidInput,
"[]",
JSON_ARRAY_SIZE(0)
},
{
// detect incomplete string event if it's skipped
"\"ABC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// detect incomplete string event if it's skipped
"'ABC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// handle escaped quotes
"'A\\'BC'",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// handle escaped quotes
"\"A\\\"BC\"",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// detect incomplete string in presence of escaped quotes
"'A\\'BC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// detect incomplete string in presence of escaped quotes
"\"A\\\"BC",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// skip empty array
"[]",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// skip empty array with spaces
" [ ] ",
"false",
10,
DeserializationError::Ok,
"null",
0
},
{
// bubble up element error even if array is skipped
"[1,'2,3]",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// bubble up member error even if object is skipped
"{'hello':'worl}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// bubble up colon error even if object is skipped
"{'hello','world'}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0
},
{
// bubble up key error even if object is skipped
"{'hello:1}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// detect invalid value in skipped object
"{'hello':!}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0
},
{
// ignore invalid value in skipped object
"{'hello':\\}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0
},
{
// check nesting limit even for ignored objects
"{}",
"false",
0,
DeserializationError::TooDeep,
"null",
0
},
{
// check nesting limit even for ignored objects
"{'hello':{}}",
"false",
1,
DeserializationError::TooDeep,
"null",
0
},
{
// check nesting limit even for ignored values in objects
"{'hello':{}}",
"{}",
1,
DeserializationError::TooDeep,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// check nesting limit even for ignored arrays
"[]",
"false",
0,
DeserializationError::TooDeep,
"null",
0
},
{
// check nesting limit even for ignored arrays
"[[]]",
"false",
1,
DeserializationError::TooDeep,
"null",
0
},
{
// check nesting limit even for ignored values in arrays
"[[]]",
"[]",
1,
DeserializationError::TooDeep,
"[]",
JSON_ARRAY_SIZE(0)
},
{
// supports back-slash at the end of skipped string
"\"hell\\",
"false",
1,
DeserializationError::IncompleteInput,
"null",
0
},
{
// invalid comment at after an element in a skipped array
"[1/]",
"false",
10,
DeserializationError::InvalidInput,
"null",
0
},
{
// incomplete comment at after an element in a skipped array
"[1/*]",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// missing comma in a skipped array
"[1 2]",
"false",
10,
DeserializationError::InvalidInput,
"null",
0
},
{
// invalid comment at the beginning of array
"[/1]",
"[false]",
10,
DeserializationError::InvalidInput,
"[]",
JSON_ARRAY_SIZE(0)
},
{
// incomplete comment at the begining of an array
"[/*]",
"[false]",
10,
DeserializationError::IncompleteInput,
"[]",
JSON_ARRAY_SIZE(0)
},
{
// invalid comment before key
"{/1:2}",
"{}",
10,
DeserializationError::InvalidInput,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// incomplete comment before key
"{/*:2}",
"{}",
10,
DeserializationError::IncompleteInput,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// invalid comment after key
"{\"example\"/1:2}",
"{}",
10,
DeserializationError::InvalidInput,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// incomplete comment after key
"{\"example\"/*:2}",
"{}",
10,
DeserializationError::IncompleteInput,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// invalid comment after colon
"{\"example\":/12}",
"{}",
10,
DeserializationError::InvalidInput,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// incomplete comment after colon
"{\"example\":/*2}",
"{}",
10,
DeserializationError::IncompleteInput,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// comment next to an integer
"{\"ignore\":1//,\"example\":2\n}",
"{\"example\":true}",
10,
DeserializationError::Ok,
"{}",
JSON_OBJECT_SIZE(0)
},
{
// invalid comment after opening brace of a skipped object
"{/1:2}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0
},
{
// incomplete after opening brace of a skipped object
"{/*:2}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// invalid comment after key of a skipped object
"{\"example\"/:2}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0
},
{
// incomplete after after key of a skipped object
"{\"example\"/*:2}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
{
// invalid comment after value in a skipped object
"{\"example\":2/}",
"false",
10,
DeserializationError::InvalidInput,
"null",
0
},
{
// incomplete after after value of a skipped object
"{\"example\":2/*}",
"false",
10,
DeserializationError::IncompleteInput,
"null",
0
},
}; // clang-format on
for (size_t i = 0; i < sizeof(testCases) / sizeof(testCases[0]); i++) {
CAPTURE(i);
DynamicJsonDocument filter(256);
DynamicJsonDocument doc(256);
TestCase& tc = testCases[i];
CAPTURE(tc.filter);
REQUIRE(deserializeJson(filter, tc.filter) == DeserializationError::Ok);
CAPTURE(tc.input);
CAPTURE(tc.nestingLimit);
CHECK(deserializeJson(doc, tc.input, DeserializationOption::Filter(filter),
DeserializationOption::NestingLimit(
tc.nestingLimit)) == tc.error);
CHECK(doc.as<std::string>() == tc.output);
CHECK(doc.memoryUsage() == tc.memoryUsage);
}
}
TEST_CASE("Overloads") {
StaticJsonDocument<256> doc;
StaticJsonDocument<256> filter;
using namespace DeserializationOption;
// deserializeJson(..., Filter)
SECTION("const char*, Filter") {
deserializeJson(doc, "{}", Filter(filter));
}
SECTION("const char*, size_t, Filter") {
deserializeJson(doc, "{}", 2, Filter(filter));
}
SECTION("const std::string&, Filter") {
deserializeJson(doc, std::string("{}"), Filter(filter));
}
SECTION("std::istream&, Filter") {
std::stringstream s("{}");
deserializeJson(doc, s, Filter(filter));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("char[n], Filter") {
int i = 4;
char vla[i];
strcpy(vla, "{}");
deserializeJson(doc, vla, Filter(filter));
}
#endif
// deserializeJson(..., Filter, NestingLimit)
SECTION("const char*, Filter, NestingLimit") {
deserializeJson(doc, "{}", Filter(filter), NestingLimit(5));
}
SECTION("const char*, size_t, Filter, NestingLimit") {
deserializeJson(doc, "{}", 2, Filter(filter), NestingLimit(5));
}
SECTION("const std::string&, Filter, NestingLimit") {
deserializeJson(doc, std::string("{}"), Filter(filter), NestingLimit(5));
}
SECTION("std::istream&, Filter, NestingLimit") {
std::stringstream s("{}");
deserializeJson(doc, s, Filter(filter), NestingLimit(5));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("char[n], Filter, NestingLimit") {
int i = 4;
char vla[i];
strcpy(vla, "{}");
deserializeJson(doc, vla, Filter(filter), NestingLimit(5));
}
#endif
// deserializeJson(..., NestingLimit, Filter)
SECTION("const char*, NestingLimit, Filter") {
deserializeJson(doc, "{}", NestingLimit(5), Filter(filter));
}
SECTION("const char*, size_t, NestingLimit, Filter") {
deserializeJson(doc, "{}", 2, NestingLimit(5), Filter(filter));
}
SECTION("const std::string&, NestingLimit, Filter") {
deserializeJson(doc, std::string("{}"), NestingLimit(5), Filter(filter));
}
SECTION("std::istream&, NestingLimit, Filter") {
std::stringstream s("{}");
deserializeJson(doc, s, NestingLimit(5), Filter(filter));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("char[n], NestingLimit, Filter") {
int i = 4;
char vla[i];
strcpy(vla, "{}");
deserializeJson(doc, vla, NestingLimit(5), Filter(filter));
}
#endif
}

View File

@ -13,7 +13,9 @@ TEST_CASE("Truncated JSON input") {
// true
"t", "tr", "tru",
// null
"n", "nu", "nul"};
"n", "nu", "nul",
// object
"{", "{a", "{a:", "{a:1", "{a:1,", "{a:1,"};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
DynamicJsonDocument doc(4096);

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

@ -15,6 +15,14 @@ TEST_CASE("Valid JSON strings value") {
TestCase testCases[] = {
{"\"hello world\"", "hello world"},
{"\'hello world\'", "hello world"},
{"'\"'", "\""},
{"'\\\\'", "\\"},
{"'\\/'", "/"},
{"'\\b'", "\b"},
{"'\\f'", "\f"},
{"'\\n'", "\n"},
{"'\\r'", "\r"},
{"'\\t'", "\t"},
{"\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"", "1\"2\\3/4\b5\f6\n7\r8\t9"},
{"'\\u0041'", "A"},
{"'\\u00e4'", "\xc3\xa4"}, // ä
@ -33,8 +41,8 @@ TEST_CASE("Valid JSON strings value") {
const TestCase& testCase = testCases[i];
CAPTURE(testCase.input);
DeserializationError err = deserializeJson(doc, testCase.input);
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<std::string>() == testCase.expectedOutput);
CHECK(err == DeserializationError::Ok);
CHECK(doc.as<std::string>() == testCase.expectedOutput);
}
}
@ -54,7 +62,7 @@ TEST_CASE("Truncated JSON string") {
TEST_CASE("Invalid JSON string") {
const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'",
"'\\u000G'", "'\\u000/'", "\\x1234"};
"'\\u000G'", "'\\u000/'", "'\\x1234'"};
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
DynamicJsonDocument doc(4096);
@ -66,10 +74,16 @@ TEST_CASE("Invalid JSON string") {
}
}
TEST_CASE("Not enough room to duplicate the string") {
DynamicJsonDocument doc(4);
TEST_CASE("Not enough room to save the key") {
DynamicJsonDocument doc(JSON_OBJECT_SIZE(1) + 8);
REQUIRE(deserializeJson(doc, "\"hello world!\"") ==
DeserializationError::NoMemory);
REQUIRE(doc.isNull() == true);
SECTION("Quoted string") {
REQUIRE(deserializeJson(doc, "{\"accuracy\":1}") ==
DeserializationError::NoMemory);
}
SECTION("Non-quoted string") {
REQUIRE(deserializeJson(doc, "{accuracy:1}") ==
DeserializationError::NoMemory);
}
}

View File

@ -8,9 +8,11 @@
#include <sstream>
using ARDUINOJSON_NAMESPACE::addPadding;
using ARDUINOJSON_NAMESPACE::move;
class SpyingAllocator {
public:
SpyingAllocator(const SpyingAllocator& src) : _log(src._log) {}
SpyingAllocator(std::ostream& log) : _log(log) {}
void* allocate(size_t n) {
@ -28,22 +30,128 @@ class SpyingAllocator {
std::ostream& _log;
};
typedef BasicJsonDocument<SpyingAllocator> MyJsonDocument;
class ControllableAllocator {
public:
ControllableAllocator() : _enabled(true) {}
void* allocate(size_t n) {
return _enabled ? malloc(n) : 0;
}
void deallocate(void* p) {
free(p);
}
void disable() {
_enabled = false;
}
private:
bool _enabled;
};
TEST_CASE("BasicJsonDocument") {
std::stringstream log;
SECTION("Construct/Destruct") {
{ MyJsonDocument doc(4096, log); }
{ BasicJsonDocument<SpyingAllocator> doc(4096, log); }
REQUIRE(log.str() == "A4096F");
}
SECTION("Copy construct") {
{
MyJsonDocument doc1(4096, log);
BasicJsonDocument<SpyingAllocator> doc1(4096, log);
doc1.set(std::string("The size of this string is 32!!"));
MyJsonDocument doc2(doc1);
BasicJsonDocument<SpyingAllocator> doc2(doc1);
REQUIRE(doc1.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc2.capacity() == 4096);
}
REQUIRE(log.str() == "A4096A4096FF");
}
#if ARDUINOJSON_HAS_RVALUE_REFERENCES
SECTION("Move construct") {
{
BasicJsonDocument<SpyingAllocator> doc1(4096, log);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument<SpyingAllocator> doc2(move(doc1));
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc1.as<std::string>() == "null");
REQUIRE(doc1.capacity() == 0);
REQUIRE(doc2.capacity() == 4096);
}
REQUIRE(log.str() == "A4096F");
}
#endif
SECTION("Copy assign") {
{
BasicJsonDocument<SpyingAllocator> doc1(4096, log);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument<SpyingAllocator> doc2(8, log);
doc2 = doc1;
REQUIRE(doc1.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc2.capacity() == 4096);
}
REQUIRE(log.str() == "A4096A8FA4096FF");
}
#if ARDUINOJSON_HAS_RVALUE_REFERENCES
SECTION("Move assign") {
{
BasicJsonDocument<SpyingAllocator> doc1(4096, log);
doc1.set(std::string("The size of this string is 32!!"));
BasicJsonDocument<SpyingAllocator> doc2(8, log);
doc2 = move(doc1);
REQUIRE(doc2.as<std::string>() == "The size of this string is 32!!");
REQUIRE(doc1.as<std::string>() == "null");
REQUIRE(doc1.capacity() == 0);
REQUIRE(doc2.capacity() == 4096);
}
REQUIRE(log.str() == "A4096A8FF");
}
#endif
SECTION("garbageCollect()") {
BasicJsonDocument<ControllableAllocator> doc(4096);
SECTION("when allocation succeeds") {
deserializeJson(doc, "{\"blanket\":1,\"dancing\":2}");
REQUIRE(doc.capacity() == 4096);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2) + 16);
doc.remove("blanket");
bool result = doc.garbageCollect();
REQUIRE(result == true);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 8);
REQUIRE(doc.capacity() == 4096);
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
}
SECTION("when allocation fails") {
deserializeJson(doc, "{\"blanket\":1,\"dancing\":2}");
REQUIRE(doc.capacity() == 4096);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2) + 16);
doc.remove("blanket");
doc.allocator().disable();
bool result = doc.garbageCollect();
REQUIRE(result == false);
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(2) + 16);
REQUIRE(doc.capacity() == 4096);
REQUIRE(doc.as<std::string>() == "{\"dancing\":2}");
}
REQUIRE(log.str() == "A4096A32FF");
}
}

View File

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

View File

@ -91,7 +91,7 @@ TEST_CASE("DynamicJsonDocument constructor") {
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
REQUIRE(doc2.capacity() == doc1.capacity());
}
SECTION("Construct from StaticJsonDocument") {
@ -101,7 +101,7 @@ TEST_CASE("DynamicJsonDocument constructor") {
DynamicJsonDocument doc2 = doc1;
REQUIRE_JSON(doc2, "{\"hello\":\"world\"}");
REQUIRE(doc2.capacity() == addPadding(doc1.memoryUsage()));
REQUIRE(doc2.capacity() == doc1.capacity());
}
SECTION("Construct from JsonObject") {

View File

@ -209,4 +209,16 @@ TEST_CASE("StaticJsonDocument") {
REQUIRE_JSON(doc2, "42");
}
}
SECTION("garbageCollect()") {
StaticJsonDocument<256> doc;
doc[std::string("example")] = std::string("jukebox");
doc.remove("example");
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(1) + 16);
doc.garbageCollect();
REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0));
REQUIRE_JSON(doc, "{}");
}
}

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

@ -48,14 +48,17 @@ typedef BasicJsonDocument<ArmoredAllocator> ShrinkToFitTestDocument;
void testShrinkToFit(ShrinkToFitTestDocument& doc, std::string expected_json,
size_t expected_size) {
doc.shrinkToFit();
// test twice: shrinkToFit() should be idempotent
for (int i = 0; i < 2; i++) {
doc.shrinkToFit();
REQUIRE(doc.capacity() == expected_size);
REQUIRE(doc.memoryUsage() == expected_size);
REQUIRE(doc.capacity() == expected_size);
REQUIRE(doc.memoryUsage() == expected_size);
std::string json;
serializeJson(doc, json);
REQUIRE(json == expected_json);
std::string json;
serializeJson(doc, json);
REQUIRE(json == expected_json);
}
}
TEST_CASE("BasicJsonDocument::shrinkToFit()") {
@ -91,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

@ -43,3 +43,11 @@ TEST_CASE("JsonDocument automatically promotes to object") {
REQUIRE(doc["one"]["two"]["three"] == 4);
}
TEST_CASE("JsonDocument automatically promotes to array") {
DynamicJsonDocument doc(4096);
doc[2] = 2;
REQUIRE(doc.as<std::string>() == "[null,null,2]");
}

View File

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

View File

@ -15,8 +15,9 @@ TEST_CASE("JsonObject::set()") {
SECTION("doesn't copy static string in key or value") {
obj1["hello"] = "world";
obj2.set(obj1);
bool success = obj2.set(obj1);
REQUIRE(success == true);
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
REQUIRE(obj2["hello"] == std::string("world"));
}
@ -24,8 +25,9 @@ TEST_CASE("JsonObject::set()") {
SECTION("copy local string value") {
obj1["hello"] = std::string("world");
obj2.set(obj1);
bool success = obj2.set(obj1);
REQUIRE(success == true);
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
REQUIRE(obj2["hello"] == std::string("world"));
}
@ -33,8 +35,9 @@ TEST_CASE("JsonObject::set()") {
SECTION("copy local key") {
obj1[std::string("hello")] = "world";
obj2.set(obj1);
bool success = obj2.set(obj1);
REQUIRE(success == true);
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
REQUIRE(obj2["hello"] == std::string("world"));
}
@ -42,8 +45,9 @@ TEST_CASE("JsonObject::set()") {
SECTION("copy string from deserializeJson()") {
deserializeJson(doc1, "{'hello':'world'}");
obj2.set(obj1);
bool success = obj2.set(obj1);
REQUIRE(success == true);
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
REQUIRE(obj2["hello"] == std::string("world"));
}
@ -51,8 +55,9 @@ TEST_CASE("JsonObject::set()") {
SECTION("copy string from deserializeMsgPack()") {
deserializeMsgPack(doc1, "\x81\xA5hello\xA5world");
obj2.set(obj1);
bool success = obj2.set(obj1);
REQUIRE(success == true);
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
REQUIRE(obj2["hello"] == std::string("world"));
}
@ -65,4 +70,46 @@ TEST_CASE("JsonObject::set()") {
REQUIRE(doc1.memoryUsage() == doc2.memoryUsage());
REQUIRE(obj2["hello"] == std::string("world"));
}
SECTION("destination too small to store the key") {
StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc3;
JsonObject obj3 = doc3.to<JsonObject>();
obj1[std::string("hello")] = "world";
bool success = obj3.set(obj1);
REQUIRE(success == false);
REQUIRE(doc3.as<std::string>() == "{}");
}
SECTION("destination too small to store the value") {
StaticJsonDocument<JSON_OBJECT_SIZE(1)> doc3;
JsonObject obj3 = doc3.to<JsonObject>();
obj1["hello"] = std::string("world");
bool success = obj3.set(obj1);
REQUIRE(success == false);
REQUIRE(doc3.as<std::string>() == "{\"hello\":null}");
}
SECTION("destination is null") {
JsonObject null;
obj1["hello"] = "world";
bool success = null.set(obj1);
REQUIRE(success == false);
}
SECTION("source is null") {
JsonObject null;
obj1["hello"] = "world";
bool success = obj1.set(null);
REQUIRE(success == false);
}
}

View File

@ -50,4 +50,18 @@ TEST_CASE("JsonObject::operator==()") {
REQUIRE(obj1 == obj2);
REQUIRE(obj1c == obj2c);
}
SECTION("should return false when RHS is null") {
JsonObject null;
REQUIRE_FALSE(obj1 == null);
REQUIRE_FALSE(obj1c == null);
}
SECTION("should return false when LHS is null") {
JsonObject null;
REQUIRE_FALSE(null == obj2);
REQUIRE_FALSE(null == obj2c);
}
}

View File

@ -30,6 +30,11 @@ TEST_CASE("JsonObject::begin()/end()") {
REQUIRE(obj.end()->key().isNull());
REQUIRE(obj.end()->value().isNull());
}
SECTION("null JsonObject") {
JsonObject null;
REQUIRE(null.begin() == null.end());
}
}
TEST_CASE("JsonObjectConst::begin()/end()") {
@ -40,15 +45,18 @@ TEST_CASE("JsonObjectConst::begin()/end()") {
JsonObjectConst cobj = obj;
SECTION("NonConstIterator") {
SECTION("Iteration") {
JsonObjectConst::iterator it = cobj.begin();
REQUIRE(cobj.end() != it);
REQUIRE(it->key() == "ab");
REQUIRE(12 == it->value());
++it;
REQUIRE(cobj.end() != it);
REQUIRE(it->key() == "cd");
REQUIRE(34 == it->value());
JsonPairConst pair = *it;
REQUIRE(pair.key() == "cd");
REQUIRE(34 == pair.value());
++it;
REQUIRE(cobj.end() == it);
}
@ -57,4 +65,9 @@ TEST_CASE("JsonObjectConst::begin()/end()") {
REQUIRE(cobj.end()->key().isNull());
REQUIRE(cobj.end()->value().isNull());
}
SECTION("null JsonObjectConst") {
JsonObjectConst null;
REQUIRE(null.begin() == null.end());
}
}

View File

@ -69,4 +69,9 @@ TEST_CASE("JsonObject::remove()") {
REQUIRE("{\"a\":0,\"c\":2}" == result);
}
#endif
SECTION("should work on null object") {
JsonObject null;
null.remove("key");
}
}

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,5 +14,4 @@ add_executable(JsonSerializerTests
std_string.cpp
)
target_link_libraries(JsonSerializerTests catch)
add_test(JsonSerializer JsonSerializerTests)

View File

@ -31,6 +31,38 @@ TEST_CASE("serializeJson(JsonVariant)") {
SECTION("string") {
check(std::string("hello"), "\"hello\"");
SECTION("Escape quotation mark") {
check(std::string("hello \"world\""), "\"hello \\\"world\\\"\"");
}
SECTION("Escape reverse solidus") {
check(std::string("hello\\world"), "\"hello\\\\world\"");
}
SECTION("Don't escape solidus") {
check(std::string("fifty/fifty"), "\"fifty/fifty\"");
}
SECTION("Escape backspace") {
check(std::string("hello\bworld"), "\"hello\\bworld\"");
}
SECTION("Escape formfeed") {
check(std::string("hello\fworld"), "\"hello\\fworld\"");
}
SECTION("Escape linefeed") {
check(std::string("hello\nworld"), "\"hello\\nworld\"");
}
SECTION("Escape carriage return") {
check(std::string("hello\rworld"), "\"hello\\rworld\"");
}
SECTION("Escape tab") {
check(std::string("hello\tworld"), "\"hello\\tworld\"");
}
}
SECTION("SerializedValue<const char*>") {

View File

@ -24,5 +24,4 @@ add_executable(JsonVariantTests
undefined.cpp
)
target_link_libraries(JsonVariantTests 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

@ -5,106 +5,152 @@
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonVariant and strings") {
enum ErrorCode { ERROR_01 = 1, ERROR_10 = 10 };
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 == "hello");
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("enum") {
ErrorCode code = ERROR_10;
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

@ -43,10 +43,10 @@ TEST_CASE("JsonVariant::operator[]") {
SECTION("set value") {
array.add("hello");
var[0] = "world";
var[1] = "world";
REQUIRE(1 == var.size());
REQUIRE(std::string("world") == var[0]);
REQUIRE(var.size() == 2);
REQUIRE(std::string("world") == var[1]);
}
SECTION("set value in a nested object") {

View File

@ -13,5 +13,4 @@ add_executable(MemberProxyTests
subscript.cpp
)
target_link_libraries(MemberProxyTests 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

@ -22,4 +22,12 @@ TEST_CASE("MemberProxy::set()") {
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
SECTION("set(char[])") { // issue #1191
char s[] = "world";
mp.set(s);
strcpy(s, "!!!!!");
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
}

View File

@ -11,9 +11,15 @@ TEST_CASE("MemberProxy::operator[]") {
DynamicJsonDocument doc(4096);
MemberProxy<JsonDocument&, const char*> mp = doc["hello"];
SECTION("set integer") {
SECTION("set member") {
mp["world"] = 42;
REQUIRE(doc.as<std::string>() == "{\"hello\":{\"world\":42}}");
}
SECTION("set element") {
mp[2] = 42;
REQUIRE(doc.as<std::string>() == "{\"hello\":[null,null,42]}");
}
}

View File

@ -4,11 +4,10 @@
add_executable(MemoryPoolTests
allocVariant.cpp
allocString.cpp
clear.cpp
saveString.cpp
size.cpp
StringBuilder.cpp
StringCopier.cpp
)
target_link_libraries(MemoryPoolTests catch)
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,40 +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 catch)
set_target_properties(MiscTests PROPERTIES UNITY_BUILD OFF)
add_test(Misc MiscTests)
add_executable(Issue978
Issue978.cpp
)
set_target_properties(Issue978
PROPERTIES
EXCLUDE_FROM_ALL TRUE
EXCLUDE_FROM_DEFAULT_BUILD TRUE
)
add_test(
NAME
Issue978
COMMAND
${CMAKE_COMMAND} --build . --target Issue978 --config $<CONFIGURATION>
WORKING_DIRECTORY
${CMAKE_BINARY_DIR}
)
set_tests_properties(Issue978
PROPERTIES
WILL_FAIL TRUE)

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

@ -2,9 +2,17 @@
// Copyright Benoit Blanchon 2014-2020
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "custom_string.hpp"
#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/StdStringAdapter.hpp>
#include <catch.hpp>
using namespace ARDUINOJSON_NAMESPACE;
@ -12,62 +20,153 @@ TEST_CASE("ConstRamStringAdapter") {
SECTION("null") {
ConstRamStringAdapter adapter(NULL);
REQUIRE(adapter.compare("bravo") < 0);
REQUIRE(adapter.compare(NULL) == 0);
CHECK(adapter.compare("bravo") < 0);
CHECK(adapter.compare(NULL) == 0);
REQUIRE(adapter.equals(NULL));
REQUIRE_FALSE(adapter.equals("charlie"));
CHECK(adapter.equals(NULL));
CHECK_FALSE(adapter.equals("charlie"));
CHECK(adapter.size() == 0);
}
SECTION("non-null") {
ConstRamStringAdapter adapter("bravo");
REQUIRE(adapter.compare(NULL) > 0);
REQUIRE(adapter.compare("alpha") > 0);
REQUIRE(adapter.compare("bravo") == 0);
REQUIRE(adapter.compare("charlie") < 0);
CHECK(adapter.compare(NULL) > 0);
CHECK(adapter.compare("alpha") > 0);
CHECK(adapter.compare("bravo") == 0);
CHECK(adapter.compare("charlie") < 0);
REQUIRE(adapter.equals("bravo"));
REQUIRE_FALSE(adapter.equals("charlie"));
CHECK(adapter.equals("bravo"));
CHECK_FALSE(adapter.equals("charlie"));
CHECK(adapter.size() == 5);
}
}
TEST_CASE("SizedRamStringAdapter") {
SECTION("null") {
SizedRamStringAdapter adapter(NULL, 10);
CHECK(adapter.compare("bravo") < 0);
CHECK(adapter.compare(NULL) == 0);
CHECK(adapter.equals(NULL));
CHECK_FALSE(adapter.equals("charlie"));
CHECK(adapter.size() == 10);
}
SECTION("non-null") {
SizedRamStringAdapter adapter("bravo", 5);
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("FlashStringAdapter") {
SECTION("null") {
FlashStringAdapter adapter(NULL);
CHECK(adapter.compare("bravo") < 0);
CHECK(adapter.compare(NULL) == 0);
CHECK(adapter.equals(NULL));
CHECK_FALSE(adapter.equals("charlie"));
CHECK(adapter.size() == 0);
}
SECTION("non-null") {
FlashStringAdapter adapter = adaptString(F("bravo"));
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("std::string") {
std::string str("bravo");
StlStringAdapter<std::string> adapter = adaptString(str);
StdStringAdapter<std::string> adapter = adaptString(str);
REQUIRE(adapter.compare(NULL) > 0);
REQUIRE(adapter.compare("alpha") > 0);
REQUIRE(adapter.compare("bravo") == 0);
REQUIRE(adapter.compare("charlie") < 0);
CHECK(adapter.compare(NULL) > 0);
CHECK(adapter.compare("alpha") > 0);
CHECK(adapter.compare("bravo") == 0);
CHECK(adapter.compare("charlie") < 0);
REQUIRE(adapter.equals("bravo"));
REQUIRE_FALSE(adapter.equals("charlie"));
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);
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("custom_string") {
custom_string str("bravo");
StlStringAdapter<custom_string> adapter = adaptString(str);
StdStringAdapter<custom_string> adapter = adaptString(str);
REQUIRE(adapter.compare(NULL) > 0);
REQUIRE(adapter.compare("alpha") > 0);
REQUIRE(adapter.compare("bravo") == 0);
REQUIRE(adapter.compare("charlie") < 0);
CHECK(adapter.compare(NULL) > 0);
CHECK(adapter.compare("alpha") > 0);
CHECK(adapter.compare("bravo") == 0);
CHECK(adapter.compare("charlie") < 0);
REQUIRE(adapter.equals("bravo"));
REQUIRE_FALSE(adapter.equals("charlie"));
CHECK(adapter.equals("bravo"));
CHECK_FALSE(adapter.equals("charlie"));
CHECK(adapter.size() == 5);
}
TEST_CASE("IsString<T>") {
SECTION("std::string") {
REQUIRE(IsString<std::string>::value == true);
CHECK(IsString<std::string>::value == true);
}
SECTION("basic_string<wchar_t>") {
REQUIRE(IsString<std::basic_string<wchar_t> >::value == false);
CHECK(IsString<std::basic_string<wchar_t> >::value == false);
}
SECTION("custom_string") {
REQUIRE(IsString<custom_string>::value == true);
CHECK(IsString<custom_string>::value == true);
}
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

@ -7,6 +7,9 @@
using namespace ARDUINOJSON_NAMESPACE;
class EmptyClass {};
enum EmptyEnum {};
TEST_CASE("Polyfills/type_traits") {
SECTION("is_base_of") {
REQUIRE_FALSE(
@ -26,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);
@ -48,6 +69,30 @@ TEST_CASE("Polyfills/type_traits") {
CHECK(is_unsigned<double>::value == false);
}
SECTION("is_convertible") {
CHECK((is_convertible<short, int>::value == true));
CHECK((is_convertible<int, int>::value == true));
CHECK((is_convertible<EmptyEnum, int>::value == true));
CHECK((is_convertible<int*, int>::value == false));
CHECK((is_convertible<EmptyClass, int>::value == false));
}
SECTION("is_class") {
CHECK((is_class<int>::value == false));
CHECK((is_class<EmptyEnum>::value == false));
CHECK((is_class<int*>::value == false));
CHECK((is_class<EmptyClass>::value == true));
}
SECTION("is_enum") {
CHECK(is_enum<int>::value == false);
CHECK(is_enum<EmptyEnum>::value == true);
CHECK(is_enum<int*>::value == false);
CHECK(is_enum<EmptyClass>::value == false);
CHECK(is_enum<bool>::value == false);
CHECK(is_enum<double>::value == false);
}
SECTION("IsVisitable") {
CHECK(IsVisitable<DeserializationError>::value == false);
CHECK(IsVisitable<JsonPair>::value == false);

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

@ -6,8 +6,6 @@
#include <string>
using namespace ARDUINOJSON_NAMESPACE;
struct custom_char_traits : std::char_traits<char> {};
struct custom_allocator : std::allocator<char> {};
typedef std::basic_string<char, custom_char_traits, custom_allocator>

View File

@ -0,0 +1,23 @@
#include <ArduinoJson/Namespace.hpp>
// Issue #1198: strcmp() implementation that returns a value larger than 8-bit
namespace ARDUINOJSON_NAMESPACE {
int strcmp(const char* a, const char* b) {
int result = ::strcmp(a, b);
if (result > 0)
return 2147483647;
if (result < 0)
return -214748364;
return 0;
}
int strncmp(const char* a, const char* b, size_t n) {
int result = ::strncmp(a, b, n);
if (result > 0)
return 2147483647;
if (result < 0)
return -214748364;
return 0;
}
} // namespace ARDUINOJSON_NAMESPACE

View File

@ -9,20 +9,23 @@ add_executable(MixedConfigurationTests
cpp11.cpp
decode_unicode_0.cpp
decode_unicode_1.cpp
enable_alignment_0.cpp
enable_alignment_1.cpp
enable_comments_0.cpp
enable_comments_1.cpp
enable_infinity_0.cpp
enable_infinity_1.cpp
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
enable_progmem_1.cpp
enable_comments_1.cpp
enable_comments_0.cpp
)
target_link_libraries(MixedConfigurationTests catch)
set_target_properties(MixedConfigurationTests PROPERTIES UNITY_BUILD OFF)
add_test(MixedConfiguration MixedConfigurationTests)

View File

@ -0,0 +1,41 @@
#define ARDUINOJSON_NAMESPACE ArduinoJson_NoAlignment
#define ARDUINOJSON_ENABLE_ALIGNMENT 0
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("ARDUINOJSON_ENABLE_ALIGNMENT == 0") {
using namespace ARDUINOJSON_NAMESPACE;
const size_t N = sizeof(void*);
SECTION("isAligned()") {
CHECK(isAligned(0) == true);
CHECK(isAligned(1) == true);
CHECK(isAligned(N) == true);
CHECK(isAligned(N + 1) == true);
CHECK(isAligned(2 * N) == true);
CHECK(isAligned(2 * N + 1) == true);
}
SECTION("addPadding()") {
CHECK(addPadding(0) == 0);
CHECK(addPadding(1) == 1);
CHECK(addPadding(N) == N);
CHECK(addPadding(N + 1) == N + 1);
}
SECTION("AddPadding<>") {
const size_t a = AddPadding<0>::value;
CHECK(a == 0);
const size_t b = AddPadding<1>::value;
CHECK(b == 1);
const size_t c = AddPadding<N>::value;
CHECK(c == N);
const size_t d = AddPadding<N + 1>::value;
CHECK(d == N + 1);
}
}

View File

@ -0,0 +1,40 @@
#define ARDUINOJSON_ENABLE_ALIGNMENT 1
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("ARDUINOJSON_ENABLE_ALIGNMENT == 1") {
using namespace ARDUINOJSON_NAMESPACE;
const size_t N = sizeof(void*);
SECTION("isAligned()") {
CHECK(isAligned(0) == true);
CHECK(isAligned(1) == false);
CHECK(isAligned(N) == true);
CHECK(isAligned(N + 1) == false);
CHECK(isAligned(2 * N) == true);
CHECK(isAligned(2 * N + 1) == false);
}
SECTION("addPadding()") {
CHECK(addPadding(0) == 0);
CHECK(addPadding(1) == N);
CHECK(addPadding(N) == N);
CHECK(addPadding(N + 1) == 2 * N);
}
SECTION("AddPadding<>") {
const size_t a = AddPadding<0>::value;
CHECK(a == 0);
const size_t b = AddPadding<1>::value;
CHECK(b == N);
const size_t c = AddPadding<N>::value;
CHECK(c == N);
const size_t d = AddPadding<N + 1>::value;
CHECK(d == 2 * N);
}
}

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