forked from bblanchon/ArduinoJson
Compare commits
106 Commits
Author | SHA1 | Date | |
---|---|---|---|
1f7350658e | |||
41132b701b | |||
712005219c | |||
0bd17aff8a | |||
f3f44d7812 | |||
8385d5fa3a | |||
726f8be341 | |||
fee029b86e | |||
c3504ddf0a | |||
6a878ee444 | |||
c4ec2ba88f | |||
c907ca6e5d | |||
8993a093e9 | |||
d04669d0cc | |||
05fc136915 | |||
8d37939086 | |||
6d2ad4539f | |||
5ab53f42b2 | |||
f448e805e9 | |||
67aa8efd5a | |||
61a5273aea | |||
35a39b8d8f | |||
96b6571352 | |||
74e7dd053f | |||
1f8636d762 | |||
e4cfa701d8 | |||
b85181a6db | |||
6841b80466 | |||
298864bafe | |||
4d7f03836c | |||
c63eb80b95 | |||
0c0bf80074 | |||
144ff3b06e | |||
d6c50c3596 | |||
51b177ce47 | |||
824b7a25ca | |||
2223d40640 | |||
4df29fbac1 | |||
6dc36125c2 | |||
40085609e2 | |||
ebb6d80092 | |||
764ff2cd53 | |||
8ef226bcb8 | |||
63606c0985 | |||
1600d39693 | |||
04c59985a1 | |||
7e58347fbe | |||
0e794a28a1 | |||
6fb52c3638 | |||
b72ef09451 | |||
f7de027617 | |||
bc4c2dde33 | |||
95f05dad66 | |||
3bb0a7aa8e | |||
dcf7eeef28 | |||
3b923b6e4e | |||
8050f7404b | |||
959b1d9e4c | |||
daa279d57b | |||
ae9b7926a2 | |||
1f7a5e6943 | |||
9e354803de | |||
3ea5eb3f3a | |||
ec43bf4fe9 | |||
2097ffaabf | |||
67e78f0751 | |||
1791dccbf2 | |||
40d1cfe7af | |||
4627f851ca | |||
fc9e609ab5 | |||
8b3d861a9d | |||
9ef864b27c | |||
275b80a462 | |||
763be4f266 | |||
5b812522fa | |||
9cb0ddb5e7 | |||
735bea1f47 | |||
0853b04589 | |||
c1b3705df1 | |||
2540b4e01b | |||
2641697e0b | |||
2996503b27 | |||
0214c9bcad | |||
a471aed6db | |||
af0edecddb | |||
300323cfd7 | |||
d8724e0a0b | |||
0001dabfd1 | |||
ad78001241 | |||
5a837a591e | |||
fd79d23910 | |||
1902c0ec93 | |||
85499be855 | |||
e6ddfc7afb | |||
58298ec5ba | |||
0814fc185f | |||
8f8c82d400 | |||
372b7d3d9d | |||
fbffadb2cf | |||
6e52f242b2 | |||
d2a67f362f | |||
66b12da4e7 | |||
42b0d6a83d | |||
94b1c75fd2 | |||
b04b549b34 | |||
09d4b2cd38 |
@ -3,3 +3,6 @@
|
||||
BasedOnStyle: Google
|
||||
Standard: Cpp03
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
|
||||
# Always break after if to get accurate coverage
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
|
38
.travis.yml
38
.travis.yml
@ -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"
|
||||
|
114
CHANGELOG.md
114
CHANGELOG.md
@ -1,6 +1,120 @@
|
||||
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)
|
||||
-------
|
||||
|
||||
* Fixed regression in UTF16 decoding (issue #1173)
|
||||
* Fixed `containsKey()` on `JsonVariantConst`
|
||||
* Added `getElement()` and `getMember()` to `JsonVariantConst`
|
||||
|
||||
v6.14.0 (2020-01-16)
|
||||
-------
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
||||
|
67
README.md
67
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
---
|
||||
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.14.0)
|
||||
[](https://www.ardu-badge.com/ArduinoJson/6.17.0)
|
||||
[](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
|
||||
[](https://travis-ci.org/bblanchon/ArduinoJson)
|
||||
[](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.
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: 6.14.0.{build}
|
||||
version: 6.17.0.{build}
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
|
63
examples/JsonFilterExample/JsonFilterExample.ino
Normal file
63
examples/JsonFilterExample/JsonFilterExample.ino
Normal 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 ❤❤❤❤❤
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
4
extras/ArduinoJsonConfig.cmake.in
Normal file
4
extras/ArduinoJsonConfig.cmake.in
Normal 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
100
extras/CompileOptions.cmake
Normal 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()
|
@ -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 .
|
||||
|
@ -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
|
||||
|
@ -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 .
|
||||
|
@ -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()
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
@ -10,6 +10,7 @@ rm -f $OUTPUT
|
||||
|
||||
# create zip
|
||||
7z a $OUTPUT \
|
||||
-xr!.vs \
|
||||
ArduinoJson/CHANGELOG.md \
|
||||
ArduinoJson/examples \
|
||||
ArduinoJson/src \
|
||||
|
@ -55,6 +55,7 @@ process()
|
||||
|
||||
simplify_namespaces() {
|
||||
perl -p0i -e 's|\} // namespace ARDUINOJSON_NAMESPACE\r?\nnamespace ARDUINOJSON_NAMESPACE \{\r?\n||igs' "$1"
|
||||
rm -f "$1.bak"
|
||||
}
|
||||
|
||||
cd $(dirname $0)/../..
|
||||
|
@ -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
|
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -9,7 +9,7 @@ add_executable(ElementProxyTests
|
||||
remove.cpp
|
||||
set.cpp
|
||||
size.cpp
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(ElementProxyTests catch)
|
||||
add_test(ElementProxy ElementProxyTests)
|
||||
|
@ -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\"]]");
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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\"]");
|
||||
}
|
||||
}
|
||||
|
25
extras/tests/ElementProxy/subscript.cpp
Normal file
25
extras/tests/ElementProxy/subscript.cpp
Normal 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]]");
|
||||
}
|
||||
}
|
38
extras/tests/FailingBuilds/CMakeLists.txt
Normal file
38
extras/tests/FailingBuilds/CMakeLists.txt
Normal 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)
|
13
extras/tests/FailingBuilds/Issue1189.cpp
Normal file
13
extras/tests/FailingBuilds/Issue1189.cpp
Normal 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);
|
||||
}
|
20
extras/tests/FailingBuilds/read_long_long.cpp
Normal file
20
extras/tests/FailingBuilds/read_long_long.cpp
Normal 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>();
|
||||
}
|
19
extras/tests/FailingBuilds/write_long_long.cpp
Normal file
19
extras/tests/FailingBuilds/write_long_long.cpp
Normal 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);
|
||||
}
|
8
extras/tests/Helpers/Arduino.h
Normal file
8
extras/tests/Helpers/Arduino.h
Normal 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"
|
@ -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;
|
42
extras/tests/Helpers/progmem_emulation.hpp
Normal file
42
extras/tests/Helpers/progmem_emulation.hpp
Normal 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)));
|
@ -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)
|
||||
|
68
extras/tests/IntegrationTests/openweathermap.cpp
Normal file
68
extras/tests/IntegrationTests/openweathermap.cpp
Normal file
File diff suppressed because one or more lines are too long
@ -19,5 +19,4 @@ add_executable(JsonArrayTests
|
||||
undefined.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonArrayTests catch)
|
||||
add_test(JsonArray JsonArrayTests)
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
}
|
||||
|
750
extras/tests/JsonDeserializer/filter.cpp
Normal file
750
extras/tests/JsonDeserializer/filter.cpp
Normal 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
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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") {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -15,12 +15,23 @@ 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"}, // ä
|
||||
{"'\\u00E4'", "\xc3\xa4"}, // ä
|
||||
{"'\\u3042'", "\xe3\x81\x82"}, // あ
|
||||
{"'\\ud83d\\udda4'", "\xf0\x9f\x96\xa4"}, // 🖤
|
||||
{"'\\uF053'", "\xef\x81\x93"}, // issue #1173
|
||||
{"'\\uF015'", "\xef\x80\x95"}, // issue #1173
|
||||
{"'\\uF054'", "\xef\x81\x94"}, // issue #1173
|
||||
};
|
||||
const size_t testCount = sizeof(testCases) / sizeof(testCases[0]);
|
||||
|
||||
@ -30,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,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);
|
||||
@ -63,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);
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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") {
|
||||
|
@ -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, "{}");
|
||||
}
|
||||
}
|
||||
|
79
extras/tests/JsonDocument/overflowed.cpp
Normal file
79
extras/tests/JsonDocument/overflowed.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -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") {
|
||||
|
@ -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]");
|
||||
}
|
||||
|
@ -19,5 +19,4 @@ add_executable(JsonObjectTests
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonObjectTests catch)
|
||||
add_test(JsonObject JsonObjectTests)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -14,5 +14,4 @@ add_executable(JsonSerializerTests
|
||||
std_string.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonSerializerTests catch)
|
||||
add_test(JsonSerializer JsonSerializerTests)
|
||||
|
@ -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*>") {
|
||||
|
@ -24,5 +24,4 @@ add_executable(JsonVariantTests
|
||||
undefined.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(JsonVariantTests catch)
|
||||
add_test(JsonVariant JsonVariantTests)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -26,3 +26,19 @@ TEST_CASE("JsonVariant::containsKey()") {
|
||||
REQUIRE(var.containsKey(std::string("world")) == false);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JsonVariantConst::containsKey()") {
|
||||
DynamicJsonDocument doc(4096);
|
||||
doc["hello"] = "world";
|
||||
JsonVariantConst cvar = doc.as<JsonVariant>();
|
||||
|
||||
SECTION("containsKey(const char*) returns true") {
|
||||
REQUIRE(cvar.containsKey("hello") == true);
|
||||
REQUIRE(cvar.containsKey("world") == false);
|
||||
}
|
||||
|
||||
SECTION("containsKey(std::string) returns true") {
|
||||
REQUIRE(cvar.containsKey(std::string("hello")) == true);
|
||||
REQUIRE(cvar.containsKey(std::string("world")) == false);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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>());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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") {
|
||||
|
@ -13,5 +13,4 @@ add_executable(MemberProxyTests
|
||||
subscript.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(MemberProxyTests catch)
|
||||
add_test(MemberProxy MemberProxyTests)
|
||||
|
@ -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"]);
|
||||
}
|
||||
}
|
||||
|
@ -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\"}");
|
||||
}
|
||||
}
|
||||
|
@ -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]}");
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
84
extras/tests/MemoryPool/StringCopier.cpp
Normal file
84
extras/tests/MemoryPool/StringCopier.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
|
81
extras/tests/MemoryPool/saveString.cpp
Normal file
81
extras/tests/MemoryPool/saveString.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -3,39 +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
|
||||
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)
|
||||
|
60
extras/tests/Misc/JsonString.cpp
Normal file
60
extras/tests/Misc/JsonString.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
68
extras/tests/Misc/Utf16.cpp
Normal file
68
extras/tests/Misc/Utf16.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
// ArduinoJson - arduinojson.org
|
||||
// Copyright Benoit Blanchon 2014-2020
|
||||
// MIT License
|
||||
|
||||
#include <ArduinoJson/Json/Utf16.hpp>
|
||||
#include <catch.hpp>
|
||||
|
||||
using namespace ARDUINOJSON_NAMESPACE;
|
||||
|
||||
static void testUtf16Codepoint(uint16_t codeunit, uint32_t expectedCodepoint) {
|
||||
Utf16::Codepoint cp;
|
||||
REQUIRE(cp.append(codeunit) == true);
|
||||
REQUIRE(cp.value() == expectedCodepoint);
|
||||
}
|
||||
|
||||
static void testUtf16Codepoint(uint16_t codeunit1, uint16_t codeunit2,
|
||||
uint32_t expectedCodepoint) {
|
||||
Utf16::Codepoint cp;
|
||||
REQUIRE(cp.append(codeunit1) == false);
|
||||
REQUIRE(cp.append(codeunit2) == true);
|
||||
REQUIRE(cp.value() == expectedCodepoint);
|
||||
}
|
||||
|
||||
TEST_CASE("Utf16::Codepoint()") {
|
||||
SECTION("U+0000") {
|
||||
testUtf16Codepoint(0x0000, 0x000000);
|
||||
}
|
||||
|
||||
SECTION("U+0001") {
|
||||
testUtf16Codepoint(0x0001, 0x000001);
|
||||
}
|
||||
|
||||
SECTION("U+D7FF") {
|
||||
testUtf16Codepoint(0xD7FF, 0x00D7FF);
|
||||
}
|
||||
|
||||
SECTION("U+E000") {
|
||||
testUtf16Codepoint(0xE000, 0x00E000);
|
||||
}
|
||||
|
||||
SECTION("U+FFFF") {
|
||||
testUtf16Codepoint(0xFFFF, 0x00FFFF);
|
||||
}
|
||||
|
||||
SECTION("U+010000") {
|
||||
testUtf16Codepoint(0xD800, 0xDC00, 0x010000);
|
||||
}
|
||||
|
||||
SECTION("U+010001") {
|
||||
testUtf16Codepoint(0xD800, 0xDC01, 0x010001);
|
||||
}
|
||||
|
||||
SECTION("U+0103FF") {
|
||||
testUtf16Codepoint(0xD800, 0xDFFF, 0x0103FF);
|
||||
}
|
||||
|
||||
SECTION("U+010400") {
|
||||
testUtf16Codepoint(0xD801, 0xDC00, 0x010400);
|
||||
}
|
||||
|
||||
SECTION("U+010400") {
|
||||
testUtf16Codepoint(0xDBFF, 0xDC00, 0x10FC00);
|
||||
}
|
||||
|
||||
SECTION("U+10FFFF") {
|
||||
testUtf16Codepoint(0xDBFF, 0xDFFF, 0x10FFFF);
|
||||
}
|
||||
}
|
@ -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()") {
|
||||
|
103
extras/tests/Misc/arithmeticCompare.cpp
Normal file
103
extras/tests/Misc/arithmeticCompare.cpp
Normal 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));
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
23
extras/tests/Misc/weird_strcmp.hpp
Normal file
23
extras/tests/Misc/weird_strcmp.hpp
Normal 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
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user