Initial commit of Arduino libraries

This commit is contained in:
Sam
2025-05-23 10:47:41 +10:00
commit 5bfce5fc3e
2476 changed files with 1108481 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,112 @@
if(NOT DEFINED COVERAGE)
set(COVERAGE OFF)
endif()
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-conversion
-Wsign-promo
-Wstrict-aliasing
-Wundef
)
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.9) AND(NOT ${COVERAGE}))
add_compile_options(-g -Og)
else() # GCC 4.8
add_compile_options(
-g
-O0 # GCC 4.8 doesn't support -Og
-Wno-shadow # allow the same name for a function parameter and a member functions
-Wp,-w # Disable preprocessing warnings (see below)
)
# GCC 4.8 doesn't support __has_include, so we need to help him
add_definitions(
-DARDUINOJSON_ENABLE_STD_STRING=1
-DARDUINOJSON_ENABLE_STD_STREAM=1
)
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")
add_compile_options(-stdlib=libc++)
link_libraries(c++ m)
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.
/Zc:__cplusplus # Enable updated __cplusplus macro
)
endif()
if(MINGW)
# Static link on MinGW to avoid linking with the wrong DLLs when multiple
# versions are installed.
add_link_options(-static)
endif()

View File

@@ -0,0 +1,8 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(example)

View File

@@ -0,0 +1,8 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
idf_component_register(
SRCS "main.cpp"
INCLUDE_DIRS ""
)

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,16 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
extern "C" void app_main() {
char buffer[256];
JsonDocument doc;
doc["hello"] = "world";
serializeJson(doc, buffer);
deserializeJson(doc, buffer);
serializeMsgPack(doc, buffer);
deserializeMsgPack(doc, buffer);
}

View File

@@ -0,0 +1,10 @@
#!/bin/sh -ex
BOARD=$1
cd "$(dirname "$0")/../../"
cp extras/particle/src/smocktest.ino src/
cp extras/particle/project.properties ./
particle compile "$BOARD"

View File

@@ -0,0 +1,18 @@
#include <ArduinoJson.h>
static_assert(ARDUINOJSON_ENABLE_PROGMEM == 1, "ARDUINOJSON_ENABLE_PROGMEM");
static_assert(ARDUINOJSON_USE_LONG_LONG == 0, "ARDUINOJSON_USE_LONG_LONG");
static_assert(ARDUINOJSON_SLOT_ID_SIZE == 1, "ARDUINOJSON_SLOT_ID_SIZE");
static_assert(ARDUINOJSON_POOL_CAPACITY == 16, "ARDUINOJSON_POOL_CAPACITY");
static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 0, "ARDUINOJSON_USE_DOUBLE");
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 6, "slot size");
void setup() {}
void loop() {}

View File

@@ -0,0 +1,16 @@
#include <ArduinoJson.h>
static_assert(ARDUINOJSON_USE_LONG_LONG == 1, "ARDUINOJSON_USE_LONG_LONG");
static_assert(ARDUINOJSON_SLOT_ID_SIZE == 2, "ARDUINOJSON_SLOT_ID_SIZE");
static_assert(ARDUINOJSON_POOL_CAPACITY == 128, "ARDUINOJSON_POOL_CAPACITY");
static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 8, "slot size");
void setup() {}
void loop() {}

View File

@@ -0,0 +1,16 @@
#include <ArduinoJson.h>
static_assert(ARDUINOJSON_USE_LONG_LONG == 1, "ARDUINOJSON_USE_LONG_LONG");
static_assert(ARDUINOJSON_SLOT_ID_SIZE == 4, "ARDUINOJSON_SLOT_ID_SIZE");
static_assert(ARDUINOJSON_POOL_CAPACITY == 256, "ARDUINOJSON_POOL_CAPACITY");
static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 16,
"slot size");
int main() {}

View File

@@ -0,0 +1,15 @@
#include <ArduinoJson.h>
static_assert(ARDUINOJSON_USE_LONG_LONG == 1, "ARDUINOJSON_USE_LONG_LONG");
static_assert(ARDUINOJSON_SLOT_ID_SIZE == 2, "ARDUINOJSON_SLOT_ID_SIZE");
static_assert(ARDUINOJSON_POOL_CAPACITY == 128, "ARDUINOJSON_POOL_CAPACITY");
static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
static_assert(ArduinoJson::detail::ResourceManager::slotSize == 8, "slot size");
int main() {}

View File

@@ -0,0 +1,67 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(MSVC)
add_compile_options(-D_CRT_SECURE_NO_WARNINGS)
endif()
add_executable(msgpack_reproducer
msgpack_fuzzer.cpp
reproducer.cpp
)
target_link_libraries(msgpack_reproducer
ArduinoJson
)
add_executable(json_reproducer
json_fuzzer.cpp
reproducer.cpp
)
target_link_libraries(json_reproducer
ArduinoJson
)
macro(add_fuzzer name)
set(FUZZER "${name}_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=fuzzer -fno-sanitize-recover=all"
LINK_FLAGS "-fprofile-instr-generate -fcoverage-mapping -fsanitize=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
LABELS "Fuzzing"
)
endmacro()
# Needs Clang 6+ to compile
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6)
if(DEFINED ENV{GITHUB_ACTIONS} AND CMAKE_CXX_COMPILER_VERSION MATCHES "^11\\.")
# Clang 11 fails on GitHub Actions with the following error:
# > ERROR: UndefinedBehaviorSanitizer failed to allocate 0x0 (0) bytes of SetAlternateSignalStack (error code: 22)
# > Sanitizer CHECK failed: /build/llvm-toolchain-11-mnvtwk/llvm-toolchain-11-11.1.0/compiler-rt/lib/sanitizer_common/sanitizer_common.cpp:54 ((0 && "unable to mmap")) != (0) (0, 0)
message(WARNING "Fuzzing is disabled on GitHub Actions to workaround a bug in Clang 11")
return()
endif()
add_fuzzer(json)
add_fuzzer(msgpack)
endif()

View File

@@ -0,0 +1,22 @@
# CAUTION: this file is invoked by https://github.com/google/oss-fuzz
CXXFLAGS += -I../../src -DARDUINOJSON_DEBUG=1 -std=c++11
all: \
$(OUT)/json_fuzzer \
$(OUT)/json_fuzzer_seed_corpus.zip \
$(OUT)/json_fuzzer.options \
$(OUT)/msgpack_fuzzer \
$(OUT)/msgpack_fuzzer_seed_corpus.zip \
$(OUT)/msgpack_fuzzer.options
$(OUT)/%_fuzzer: %_fuzzer.cpp $(shell find ../../src -type f)
$(CXX) $(CXXFLAGS) $< -o$@ $(LIB_FUZZING_ENGINE)
$(OUT)/%_fuzzer_seed_corpus.zip: %_seed_corpus/*
zip -j $@ $?
$(OUT)/%_fuzzer.options:
@echo "[libfuzzer]" > $@
@echo "max_len = 256" >> $@
@echo "timeout = 10" >> $@

View File

@@ -0,0 +1,11 @@
#include <ArduinoJson.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
JsonDocument doc;
DeserializationError error = deserializeJson(doc, data, size);
if (!error) {
std::string json;
serializeJson(doc, json);
}
return 0;
}

View File

@@ -0,0 +1,10 @@
//comment
/*comment*/
[ //comment
/*comment*/"comment"/*comment*/,//comment
/*comment*/{//comment
/* comment*/"key"//comment
: //comment
"value"//comment
}/*comment*/
]//comment

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
[1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,[12,[13,[14,[15,[16,[17,[18,[19,[20,[21,[22,[23,[24,[25,[26,[27,[28,[29,[30,[31,[32,[33,[34,[35,[36,[37,[38,[39,[40,[41,[42,[43,[44,[45,[46,[47,[48,[49,[50,[51,[52,[53,[54,[55,[56,[57,[58,[59,[60,[61,[62,[63,[64,[65,[66,[67,[68,[69,[70,[71,[72,[73,[74,[75,[76,[77,[78,[79,[80,[81,[82,[83,[84,[85,[86,[87,[88,[89,[90,[91,[92,[93,[94,[95,[96,[97,[98,[99,[100,[101,[102,[103,[104,[105,[106,[107,[108,[109,[110,[111,[112,[113,[114,[115,[116,[117,[118,[119,[120]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]

View File

@@ -0,0 +1 @@
9720730739393920739

View File

@@ -0,0 +1,24 @@
[
123,
-123,
123.456,
-123.456,
12e34,
12e-34,
12e+34,
12E34,
12E-34,
12E+34,
12.34e56,
12.34e-56,
12.34e+56,
12.34E56,
12.34E-56,
12.34E+56,
NaN,
-NaN,
+NaN,
Infinity,
+Infinity,
-Infinity
]

View File

@@ -0,0 +1,53 @@
{
"coord": {
"lon": -0.13,
"lat": 51.51
},
"weather": [
{
"id": 301,
"main": "Drizzle",
"description": "drizzle",
"icon": "09n"
},
{
"id": 701,
"main": "Mist",
"description": "mist",
"icon": "50n"
},
{
"id": 741,
"main": "Fog",
"description": "fog",
"icon": "50n"
}
],
"base": "stations",
"main": {
"temp": 281.87,
"pressure": 1032,
"humidity": 100,
"temp_min": 281.15,
"temp_max": 283.15
},
"visibility": 2900,
"wind": {
"speed": 1.5
},
"clouds": {
"all": 90
},
"dt": 1483820400,
"sys": {
"type": 1,
"id": 5091,
"message": 0.0226,
"country": "GB",
"sunrise": 1483776245,
"sunset": 1483805443
},
"id": 2643743,
"name": "London",
"cod": 200
}

View File

@@ -0,0 +1,8 @@
[
"hello",
'hello',
hello,
{"hello":"world"},
{'hello':'world'},
{hello:world}
]

View File

@@ -0,0 +1,90 @@
{
"response": {
"version": "0.1",
"termsofService": "http://www.wunderground.com/weather/api/d/terms.html",
"features": {
"conditions": 1
}
},
"current_observation": {
"image": {
"url": "http://icons-ak.wxug.com/graphics/wu2/logo_130x80.png",
"title": "Weather Underground",
"link": "http://www.wunderground.com"
},
"display_location": {
"full": "San Francisco, CA",
"city": "San Francisco",
"state": "CA",
"state_name": "California",
"country": "US",
"country_iso3166": "US",
"zip": "94101",
"latitude": "37.77500916",
"longitude": "-122.41825867",
"elevation": "47.00000000"
},
"observation_location": {
"full": "SOMA - Near Van Ness, San Francisco, California",
"city": "SOMA - Near Van Ness, San Francisco",
"state": "California",
"country": "US",
"country_iso3166": "US",
"latitude": "37.773285",
"longitude": "-122.417725",
"elevation": "49 ft"
},
"estimated": {},
"station_id": "KCASANFR58",
"observation_time": "Last Updated on June 27, 5:27 PM PDT",
"observation_time_rfc822": "Wed, 27 Jun 2012 17:27:13 -0700",
"observation_epoch": "1340843233",
"local_time_rfc822": "Wed, 27 Jun 2012 17:27:14 -0700",
"local_epoch": "1340843234",
"local_tz_short": "PDT",
"local_tz_long": "America/Los_Angeles",
"local_tz_offset": "-0700",
"weather": "Partly Cloudy",
"temperature_string": "66.3 F (19.1 C)",
"temp_f": 66.3,
"temp_c": 19.1,
"relative_humidity": "65%",
"wind_string": "From the NNW at 22.0 MPH Gusting to 28.0 MPH",
"wind_dir": "NNW",
"wind_degrees": 346,
"wind_mph": 22,
"wind_gust_mph": "28.0",
"wind_kph": 35.4,
"wind_gust_kph": "45.1",
"pressure_mb": "1013",
"pressure_in": "29.93",
"pressure_trend": "+",
"dewpoint_string": "54 F (12 C)",
"dewpoint_f": 54,
"dewpoint_c": 12,
"heat_index_string": "NA",
"heat_index_f": "NA",
"heat_index_c": "NA",
"windchill_string": "NA",
"windchill_f": "NA",
"windchill_c": "NA",
"feelslike_string": "66.3 F (19.1 C)",
"feelslike_f": "66.3",
"feelslike_c": "19.1",
"visibility_mi": "10.0",
"visibility_km": "16.1",
"solarradiation": "",
"UV": "5",
"precip_1hr_string": "0.00 in ( 0 mm)",
"precip_1hr_in": "0.00",
"precip_1hr_metric": " 0",
"precip_today_string": "0.00 in (0 mm)",
"precip_today_in": "0.00",
"precip_today_metric": "0",
"icon": "partlycloudy",
"icon_url": "http://icons-ak.wxug.com/i/c/k/partlycloudy.gif",
"forecast_url": "http://www.wunderground.com/US/CA/San_Francisco.html",
"history_url": "http://www.wunderground.com/history/airport/KCASANFR58/2012/6/27/DailyHistory.html",
"ob_url": "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=37.773285,-122.417725"
}
}

View File

@@ -0,0 +1,11 @@
#include <ArduinoJson.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
JsonDocument doc;
DeserializationError error = deserializeMsgPack(doc, data, size);
if (!error) {
std::string json;
serializeMsgPack(doc, json);
}
return 0;
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
<EFBFBD>

View File

@@ -0,0 +1 @@
¥hello¥world

View File

@@ -0,0 +1 @@
<EFBFBD>

View File

@@ -0,0 +1 @@


View File

@@ -0,0 +1 @@
£one£two

View File

@@ -0,0 +1 @@
«hello world

View File

@@ -0,0 +1 @@
ハ@H<>

View File

@@ -0,0 +1 @@
Л@ !КАѓo

View File

@@ -0,0 +1 @@
<EFBFBD>

View File

@@ -0,0 +1 @@
Ҷi<EFBFBD>.

View File

@@ -0,0 +1 @@
4Vx埔゙<E59F94>

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD>

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
<EFBFBD>

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
Ùhello

View File

@@ -0,0 +1 @@
<EFBFBD>

View File

@@ -0,0 +1 @@
<EFBFBD>09

View File

@@ -0,0 +1 @@
<EFBFBD>4Vx

View File

@@ -0,0 +1 @@
4Vx埔゙<E59F94>

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD>

View File

@@ -0,0 +1,50 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
// This file is NOT use by Google's OSS fuzz
// I only use it to reproduce the bugs found
#include <stdint.h> // size_t
#include <stdio.h> // fopen et al.
#include <stdlib.h> // exit
#include <iostream>
#include <vector>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
std::vector<uint8_t> read(const char* path) {
FILE* f = fopen(path, "rb");
if (!f) {
std::cerr << "Failed to open " << path << std::endl;
exit(1);
}
fseek(f, 0, SEEK_END);
size_t size = static_cast<size_t>(ftell(f));
fseek(f, 0, SEEK_SET);
std::vector<uint8_t> buffer(size);
if (fread(buffer.data(), 1, size, f) != size) {
fclose(f);
std::cerr << "Failed to read " << path << std::endl;
exit(1);
}
fclose(f);
return buffer;
}
int main(int argc, const char* argv[]) {
if (argc < 2) {
std::cerr << "Usage: msgpack_fuzzer files" << std::endl;
return 1;
}
for (int i = 1; i < argc; i++) {
std::cout << "Loading " << argv[i] << std::endl;
std::vector<uint8_t> buffer = read(argv[i]);
LLVMFuzzerTestOneInput(buffer.data(), buffer.size());
}
return 0;
}

View File

@@ -0,0 +1 @@
name=ArduinoJsonCI

View File

@@ -0,0 +1,5 @@
#include "ArduinoJson.h"
void setup() {}
void loop() {}

View File

@@ -0,0 +1,65 @@
#!/bin/bash
set -e
RE_RELATIVE_INCLUDE='^#[[:space:]]*include[[:space:]]*"(.*)"'
RE_ABSOLUTE_INCLUDE='^#[[:space:]]*include[[:space:]]*<(ArduinoJson/.*)>'
RE_SYSTEM_INCLUDE='^#[[:space:]]*include[[:space:]]*<(.*)>'
RE_EMPTY='^(#[[:space:]]*pragma[[:space:]]+once)?[[:space:]]*(//.*)?$'
SRC_DIRECTORY="$(realpath "$(dirname $0)/../../src")"
declare -A INCLUDED
process()
{
local PARENT=$1
local FOLDER=$(dirname $1)
local SHOW_COMMENT=$2
while IFS= read -r LINE; do
if [[ $LINE =~ $RE_ABSOLUTE_INCLUDE ]]; then
local CHILD=${BASH_REMATCH[1]}
local CHILD_PATH
CHILD_PATH=$(realpath "$SRC_DIRECTORY/$CHILD")
echo "$PARENT -> $CHILD" >&2
if [[ ! ${INCLUDED[$CHILD_PATH]} ]]; then
INCLUDED[$CHILD_PATH]=true
process "$CHILD" false
fi
elif [[ $LINE =~ $RE_RELATIVE_INCLUDE ]]; then
local CHILD=${BASH_REMATCH[1]}
pushd "$FOLDER" > /dev/null
local CHILD_PATH
CHILD_PATH=$(realpath "$CHILD")
echo "$PARENT -> $CHILD" >&2
if [[ ! ${INCLUDED[$CHILD_PATH]} ]]; then
INCLUDED[$CHILD_PATH]=true
process "$CHILD" false
fi
popd > /dev/null
elif [[ $LINE =~ $RE_SYSTEM_INCLUDE ]]; then
local CHILD=${BASH_REMATCH[1]}
echo "$PARENT -> <$CHILD>" >&2
if [[ ! ${INCLUDED[$CHILD]} ]]; then
echo "#include <$CHILD>"
INCLUDED[$CHILD]=true
fi
elif [[ "${SHOW_COMMENT}" = "true" ]] ; then
echo "$LINE"
elif [[ ! $LINE =~ $RE_EMPTY ]]; then
echo "$LINE"
fi
done < $PARENT
}
simplify_namespaces() {
perl -p0i -e 's|ARDUINOJSON_END_PUBLIC_NAMESPACE\r?\nARDUINOJSON_BEGIN_PUBLIC_NAMESPACE\r?\n||igs' "$1"
perl -p0i -e 's|ARDUINOJSON_END_PRIVATE_NAMESPACE\r?\nARDUINOJSON_BEGIN_PRIVATE_NAMESPACE\r?\n||igs' "$1"
rm -f "$1.bak"
}
INCLUDED=()
INPUT=$1
OUTPUT=$2
process "$INPUT" true > "$OUTPUT"
simplify_namespaces "$OUTPUT"

View File

@@ -0,0 +1,29 @@
#!/usr/bin/awk -f
# Start echoing after the first list item
/\* / {
STARTED=1
EMPTY_LINE=0
}
# Remember if we have seen an empty line
/^[[:space:]]*$/ {
EMPTY_LINE=1
}
# Exit when seeing a new version number
/^v[[:digit:]]/ {
if (STARTED) exit
}
# Print if the line is not empty
# and restore the empty line we have skipped
!/^[[:space:]]*$/ {
if (STARTED) {
if (EMPTY_LINE) {
print ""
EMPTY_LINE=0
}
print
}
}

View File

@@ -0,0 +1,18 @@
#!/bin/bash
set -eu
VERSION="$1"
CHANGELOG="$2"
ARDUINOJSON_H="$3"
cat << END
---
branch: v7
version: $VERSION
date: '$(date +'%Y-%m-%d')'
$(extras/scripts/wandbox/publish.sh "$ARDUINOJSON_H")
---
$(extras/scripts/extract_changes.awk "$CHANGELOG")
END

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -eu
SOURCE_DIR="$(dirname "$0")/../.."
WORK_DIR=$(mktemp -d)
trap 'rm -rf "$WORK_DIR"' EXIT
cp "$SOURCE_DIR/README.md" "$WORK_DIR/README.md"
cp "$SOURCE_DIR/CHANGELOG.md" "$WORK_DIR/CHANGELOG.md"
cp "$SOURCE_DIR/library.properties" "$WORK_DIR/library.properties"
cp "$SOURCE_DIR/LICENSE.txt" "$WORK_DIR/LICENSE.txt"
cp -r "$SOURCE_DIR/src" "$WORK_DIR/"
cp -r "$SOURCE_DIR/examples" "$WORK_DIR/"
cd "$WORK_DIR"
particle library upload
particle library publish

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env bash
set -eu
which awk sed jq curl perl >/dev/null
cd "$(dirname "$0")/../.."
if ! git diff --quiet --exit-code; then
echo "Repository contains uncommitted changes"
exit
fi
VERSION="$1"
DATE=$(date +%F)
TAG="v$VERSION"
VERSION_REGEX='[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9]+)?'
STARS=$(curl -s https://api.github.com/repos/bblanchon/ArduinoJson | jq '.stargazers_count')
update_version_in_source () {
IFS=".-" read MAJOR MINOR REVISION EXTRA < <(echo "$VERSION")
UNDERLINE=$(printf -- '-%.0s' $(seq 1 ${#TAG}))
sed -i~ -bE "1,20{s/$VERSION_REGEX/$VERSION/g}" README.md
rm README.md~
sed -i~ -bE "4s/HEAD/$TAG ($DATE)/; 5s/-+/$UNDERLINE/" 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 \
-e "s/\"version\":.*$/\"version\": \"$VERSION\",/" \
-e "s/[0-9]+ stars/$STARS stars/" \
library.json
rm library.json~
sed -i~ -bE \
-e "s/version=.*$/version=$VERSION/" \
-e "s/[0-9]+ stars/$STARS stars/" \
library.properties
rm library.properties~
sed -i~ -bE "s/version: .*$/version: $VERSION.{build}/" appveyor.yml
rm appveyor.yml~
sed -i~ -bE \
-e "s/^version: .*$/version: \"$VERSION\"/" \
-e "s/[0-9]+ stars/$STARS stars/" \
idf_component.yml
rm idf_component.yml~
sed -i~ -bE \
-e "s/ARDUINOJSON_VERSION .*$/ARDUINOJSON_VERSION \"$VERSION\"/" \
-e "s/ARDUINOJSON_VERSION_MAJOR .*$/ARDUINOJSON_VERSION_MAJOR $MAJOR/" \
-e "s/ARDUINOJSON_VERSION_MINOR .*$/ARDUINOJSON_VERSION_MINOR $MINOR/" \
-e "s/ARDUINOJSON_VERSION_REVISION .*$/ARDUINOJSON_VERSION_REVISION $REVISION/" \
-e "s/ARDUINOJSON_VERSION_MACRO .*$/ARDUINOJSON_VERSION_MACRO V$MAJOR$MINOR$REVISION/" \
src/ArduinoJson/version.hpp
rm src/ArduinoJson/version.hpp*~
}
commit_new_version () {
git add src/ArduinoJson/version.hpp README.md CHANGELOG.md library.json library.properties appveyor.yml CMakeLists.txt idf_component.yml
git commit -m "Set version to $VERSION"
}
add_tag () {
CHANGES=$(awk '/\* /{ FOUND=1; print; next } { if (FOUND) exit}' CHANGELOG.md)
git tag -m "ArduinoJson $VERSION"$'\n'"$CHANGES" "$TAG"
}
push () {
git push --follow-tags
}
update_version_in_source
commit_new_version
add_tag
push
extras/scripts/build-single-header.sh "src/ArduinoJson.h" "../ArduinoJson-$TAG.h"
extras/scripts/build-single-header.sh "src/ArduinoJson.hpp" "../ArduinoJson-$TAG.hpp"
extras/scripts/get-release-page.sh "$VERSION" "CHANGELOG.md" "../ArduinoJson-$TAG.h" > "../ArduinoJson-$TAG.md"
echo "You can now copy ../ArduinoJson-$TAG.md into arduinojson.org/collections/_versions/$VERSION.md"

View File

@@ -0,0 +1,42 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to generate a JSON document with ArduinoJson.
#include <iostream>
#include "ArduinoJson.h"
int main() {
// Allocate the JSON document
JsonDocument doc;
// Add values in the document.
doc["sensor"] = "gps";
doc["time"] = 1351824120;
// Add an array
JsonArray data = doc["data"].to<JsonArray>();
data.add(48.756080);
data.add(2.302038);
// Generate the minified JSON and send it to STDOUT
serializeJson(doc, std::cout);
// The above line prints:
// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
// Start a new line
std::cout << std::endl;
// Generate the prettified JSON and send it to STDOUT
serializeJsonPretty(doc, std::cout);
// The above line prints:
// {
// "sensor": "gps",
// "time": 1351824120,
// "data": [
// 48.756080,
// 2.302038
// ]
// }
}

View File

@@ -0,0 +1,43 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to deserialize a JSON document with ArduinoJson.
#include <iostream>
#include "ArduinoJson.h"
int main() {
// Allocate the JSON document
JsonDocument doc;
// JSON input string
const char* json =
"{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, json);
// Test if parsing succeeds
if (error) {
std::cerr << "deserializeJson() failed: " << error.c_str() << std::endl;
return 1;
}
// Fetch the values
//
// Most of the time, you can rely on the implicit casts.
// In other case, you can do doc["time"].as<long>();
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
// Print the values
std::cout << sensor << std::endl;
std::cout << time << std::endl;
std::cout << latitude << std::endl;
std::cout << longitude << std::endl;
return 0;
}

View File

@@ -0,0 +1,51 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
//
// This example shows how to generate a JSON document with ArduinoJson.
#include <iostream>
#include "ArduinoJson.h"
int main() {
// Allocate the JSON document
JsonDocument doc;
// The MessagePack input string
uint8_t input[] = {131, 166, 115, 101, 110, 115, 111, 114, 163, 103, 112, 115,
164, 116, 105, 109, 101, 206, 80, 147, 50, 248, 164, 100,
97, 116, 97, 146, 203, 64, 72, 96, 199, 58, 188, 148,
112, 203, 64, 2, 106, 146, 230, 33, 49, 169};
// This MessagePack document contains:
// {
// "sensor": "gps",
// "time": 1351824120,
// "data": [48.75608, 2.302038]
// }
// Parse the input
DeserializationError error = deserializeMsgPack(doc, input);
// Test if parsing succeeds
if (error) {
std::cerr << "deserializeMsgPack() failed: " << error.c_str() << std::endl;
return 1;
}
// Fetch the values
//
// Most of the time, you can rely on the implicit casts.
// In other case, you can do doc["time"].as<long>();
const char* sensor = doc["sensor"];
long time = doc["time"];
double latitude = doc["data"][0];
double longitude = doc["data"][1];
// Print the values
std::cout << sensor << std::endl;
std::cout << time << std::endl;
std::cout << latitude << std::endl;
std::cout << longitude << std::endl;
return 0;
}

View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -eu
ARDUINOJSON_H="$1"
read_string() {
jq --slurp --raw-input '.' "$1"
}
compile() {
FILE_PATH="$(dirname $0)/$1.cpp"
cat >parameters.json <<END
{
"code":$(read_string "$FILE_PATH"),
"codes": [{"file":"ArduinoJson.h","code":$(read_string "$ARDUINOJSON_H")}],
"options": "warning,c++11",
"compiler": "gcc-head",
"save": true
}
END
URL=$(curl -sS -H "Content-type: application/json" -d @parameters.json https://wandbox.org/api/compile.json | jq --raw-output .url)
rm parameters.json
[ -n "$URL" ] && echo "$1: $URL"
}
compile "JsonGeneratorExample"
compile "JsonParserExample"
compile "MsgPackParserExample"

View File

@@ -0,0 +1,36 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
link_libraries(ArduinoJson)
# Failing builds should only link with ArduinoJson, not catch
add_subdirectory(FailingBuilds)
add_subdirectory(catch)
link_libraries(catch)
include_directories(Helpers)
add_subdirectory(Cpp17)
add_subdirectory(Cpp20)
add_subdirectory(Deprecated)
add_subdirectory(IntegrationTests)
add_subdirectory(JsonArray)
add_subdirectory(JsonArrayConst)
add_subdirectory(JsonDeserializer)
add_subdirectory(JsonDocument)
add_subdirectory(JsonObject)
add_subdirectory(JsonObjectConst)
add_subdirectory(JsonSerializer)
add_subdirectory(JsonVariant)
add_subdirectory(JsonVariantConst)
add_subdirectory(ResourceManager)
add_subdirectory(Misc)
add_subdirectory(MixedConfiguration)
add_subdirectory(MsgPackDeserializer)
add_subdirectory(MsgPackSerializer)
add_subdirectory(Numbers)
add_subdirectory(TextFormatter)

View File

@@ -0,0 +1,29 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
if(MSVC_VERSION LESS 1910)
return()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
return()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7)
return()
endif()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(Cpp17Tests
string_view.cpp
)
add_test(Cpp17 Cpp17Tests)
set_tests_properties(Cpp17
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,113 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
// we expect ArduinoJson.h to include <string_view>
// but we don't want it to included accidentally
#undef ARDUINO
#define ARDUINOJSON_ENABLE_STD_STREAM 0
#define ARDUINOJSON_ENABLE_STD_STRING 0
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
#if !ARDUINOJSON_ENABLE_STRING_VIEW
# error ARDUINOJSON_ENABLE_STRING_VIEW must be set to 1
#endif
using ArduinoJson::detail::sizeofArray;
TEST_CASE("string_view") {
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("deserializeJson()") {
auto err = deserializeJson(doc, std::string_view("123", 2));
REQUIRE(err == DeserializationError::Ok);
REQUIRE(doc.as<int>() == 12);
}
SECTION("JsonDocument::set()") {
doc.set(std::string_view("123", 2));
REQUIRE(doc.as<std::string_view>() == "12");
}
SECTION("JsonDocument::operator[]() const") {
doc["ab"] = "Yes";
doc["abc"] = "No";
REQUIRE(doc[std::string_view("abc", 2)] == "Yes");
}
SECTION("JsonDocument::operator[]()") {
doc[std::string_view("abc", 2)] = "Yes";
REQUIRE(doc["ab"] == "Yes");
}
SECTION("JsonVariant::operator==()") {
variant.set("A");
REQUIRE(variant == std::string_view("AX", 1));
REQUIRE_FALSE(variant == std::string_view("BX", 1));
}
SECTION("JsonVariant::operator>()") {
variant.set("B");
REQUIRE(variant > std::string_view("AX", 1));
REQUIRE_FALSE(variant > std::string_view("CX", 1));
}
SECTION("JsonVariant::operator<()") {
variant.set("B");
REQUIRE(variant < std::string_view("CX", 1));
REQUIRE_FALSE(variant < std::string_view("AX", 1));
}
SECTION("String deduplication") {
doc.add(std::string_view("example one", 7));
doc.add(std::string_view("example two", 7));
doc.add(std::string_view("example\0tree", 12));
doc.add(std::string_view("example\0tree and a half", 12));
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("example")),
Allocate(sizeofString("example tree")),
});
}
SECTION("as<std::string_view>()") {
doc["s"] = "Hello World";
doc["i"] = 42;
REQUIRE(doc["s"].as<std::string_view>() == std::string_view("Hello World"));
REQUIRE(doc["i"].as<std::string_view>() == std::string_view());
}
SECTION("is<std::string_view>()") {
doc["s"] = "Hello World";
doc["i"] = 42;
REQUIRE(doc["s"].is<std::string_view>() == true);
REQUIRE(doc["i"].is<std::string_view>() == false);
}
SECTION("String containing NUL") {
doc.set("hello\0world"_s);
REQUIRE(doc.as<std::string_view>().size() == 11);
REQUIRE(doc.as<std::string_view>() == std::string_view("hello\0world", 11));
}
}
using ArduinoJson::detail::adaptString;
TEST_CASE("StringViewAdapter") {
std::string_view str("bravoXXX", 5);
auto adapter = adaptString(str);
CHECK(stringCompare(adapter, adaptString("alpha", 5)) > 0);
CHECK(stringCompare(adapter, adaptString("bravo", 5)) == 0);
CHECK(stringCompare(adapter, adaptString("charlie", 7)) < 0);
CHECK(adapter.size() == 5);
}

View File

@@ -0,0 +1,29 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
if(MSVC_VERSION LESS 1910)
return()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10)
return()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10)
return()
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(Cpp20Tests
smoke_test.cpp
)
add_test(Cpp20 Cpp20Tests)
set_tests_properties(Cpp20
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,15 @@
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
TEST_CASE("C++20 smoke test") {
JsonDocument doc;
deserializeJson(doc, "{\"hello\":\"world\"}");
REQUIRE(doc["hello"] == "world");
std::string json;
serializeJson(doc, json);
REQUIRE(json == "{\"hello\":\"world\"}");
}

View File

@@ -0,0 +1,69 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
using ArduinoJson::detail::is_base_of;
static std::string allocatorLog;
struct CustomAllocator {
CustomAllocator() {
allocatorLog = "";
}
void* allocate(size_t n) {
allocatorLog += "A";
return malloc(n);
}
void deallocate(void* p) {
free(p);
allocatorLog += "D";
}
void* reallocate(void* p, size_t n) {
allocatorLog += "R";
return realloc(p, n);
}
};
TEST_CASE("BasicJsonDocument") {
allocatorLog.clear();
SECTION("is a JsonDocument") {
REQUIRE(
is_base_of<JsonDocument, BasicJsonDocument<CustomAllocator>>::value ==
true);
}
SECTION("deserialize / serialize") {
BasicJsonDocument<CustomAllocator> doc(256);
deserializeJson(doc, "{\"hello\":\"world\"}");
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
doc.clear();
REQUIRE(allocatorLog == "AARARDDD");
}
SECTION("copy") {
BasicJsonDocument<CustomAllocator> doc(256);
doc["hello"] = "world";
auto copy = doc;
REQUIRE(copy.as<std::string>() == "{\"hello\":\"world\"}");
REQUIRE(allocatorLog == "AA");
}
SECTION("capacity") {
BasicJsonDocument<CustomAllocator> doc(256);
REQUIRE(doc.capacity() == 256);
}
SECTION("garbageCollect()") {
BasicJsonDocument<CustomAllocator> doc(256);
doc.garbageCollect();
}
}

View File

@@ -0,0 +1,35 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
add_compile_options(
-w
)
endif()
if(MSVC)
add_compile_options(
/wd4996
)
endif()
add_executable(DeprecatedTests
add.cpp
BasicJsonDocument.cpp
containsKey.cpp
createNestedArray.cpp
createNestedObject.cpp
DynamicJsonDocument.cpp
macros.cpp
memoryUsage.cpp
shallowCopy.cpp
StaticJsonDocument.cpp
)
add_test(Deprecated DeprecatedTests)
set_tests_properties(Deprecated
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,37 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using ArduinoJson::detail::is_base_of;
TEST_CASE("DynamicJsonDocument") {
SECTION("is a JsonDocument") {
REQUIRE(is_base_of<JsonDocument, DynamicJsonDocument>::value == true);
}
SECTION("deserialize / serialize") {
DynamicJsonDocument doc(256);
deserializeJson(doc, "{\"hello\":\"world\"}");
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
SECTION("copy") {
DynamicJsonDocument doc(256);
doc["hello"] = "world";
auto copy = doc;
REQUIRE(copy.as<std::string>() == "{\"hello\":\"world\"}");
}
SECTION("capacity") {
DynamicJsonDocument doc(256);
REQUIRE(doc.capacity() == 256);
}
SECTION("garbageCollect()") {
DynamicJsonDocument doc(256);
doc.garbageCollect();
}
}

View File

@@ -0,0 +1,32 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
using ArduinoJson::detail::is_base_of;
TEST_CASE("StaticJsonDocument") {
SECTION("is a JsonDocument") {
REQUIRE(is_base_of<JsonDocument, StaticJsonDocument<256>>::value == true);
}
SECTION("deserialize / serialize") {
StaticJsonDocument<256> doc;
deserializeJson(doc, "{\"hello\":\"world\"}");
REQUIRE(doc.as<std::string>() == "{\"hello\":\"world\"}");
}
SECTION("copy") {
StaticJsonDocument<256> doc;
doc["hello"] = "world";
auto copy = doc;
REQUIRE(copy.as<std::string>() == "{\"hello\":\"world\"}");
}
SECTION("capacity") {
StaticJsonDocument<256> doc;
REQUIRE(doc.capacity() == 256);
}
}

View File

@@ -0,0 +1,38 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray::add()") {
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
array.add().set(42);
REQUIRE(doc.as<std::string>() == "[42]");
}
TEST_CASE("JsonDocument::add()") {
JsonDocument doc;
doc.add().set(42);
REQUIRE(doc.as<std::string>() == "[42]");
}
TEST_CASE("ElementProxy::add()") {
JsonDocument doc;
doc[0].add().set(42);
REQUIRE(doc.as<std::string>() == "[[42]]");
}
TEST_CASE("MemberProxy::add()") {
JsonDocument doc;
doc["x"].add().set(42);
REQUIRE(doc.as<std::string>() == "{\"x\":[42]}");
}
TEST_CASE("JsonVariant::add()") {
JsonDocument doc;
JsonVariant v = doc.add();
v.add().set(42);
REQUIRE(doc.as<std::string>() == "[[42]]");
}

View File

@@ -0,0 +1,246 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Literals.hpp"
TEST_CASE("JsonDocument::containsKey()") {
JsonDocument doc;
SECTION("returns true on object") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when value is null") {
doc["hello"] = static_cast<const char*>(0);
REQUIRE(doc.containsKey("hello") == true);
}
SECTION("returns true when key is a std::string") {
doc["hello"] = "world";
REQUIRE(doc.containsKey("hello"_s) == true);
}
SECTION("returns false on object") {
doc["world"] = "hello";
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on array") {
doc.add("hello");
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("returns false on null") {
REQUIRE(doc.containsKey("hello") == false);
}
SECTION("supports JsonVariant") {
doc["hello"] = "world";
doc["key"] = "hello";
REQUIRE(doc.containsKey(doc["key"]) == true);
REQUIRE(doc.containsKey(doc["foo"]) == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
doc["hello"] = "world";
REQUIRE(doc.containsKey(vla) == true);
}
#endif
}
TEST_CASE("MemberProxy::containsKey()") {
JsonDocument doc;
const auto& mp = doc["hello"];
SECTION("containsKey(const char*)") {
mp["key"] = "value";
REQUIRE(mp.containsKey("key") == true);
REQUIRE(mp.containsKey("key") == true);
}
SECTION("containsKey(std::string)") {
mp["key"] = "value";
REQUIRE(mp.containsKey("key"_s) == true);
REQUIRE(mp.containsKey("key"_s) == true);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
mp["hello"] = "world";
REQUIRE(mp.containsKey(vla) == true);
}
#endif
}
TEST_CASE("JsonObject::containsKey()") {
JsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = 42;
SECTION("returns true only if key is present") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("returns false after remove()") {
obj.remove("hello");
REQUIRE(false == obj.containsKey("hello"));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("key is a VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("key is a JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
SECTION("std::string") {
REQUIRE(true == obj.containsKey("hello"_s));
}
SECTION("unsigned char[]") {
unsigned char key[] = "hello";
REQUIRE(true == obj.containsKey(key));
}
}
TEST_CASE("JsonObjectConst::containsKey()") {
JsonDocument doc;
doc["hello"] = 42;
auto obj = doc.as<JsonObjectConst>();
SECTION("supports const char*") {
REQUIRE(false == obj.containsKey("world"));
REQUIRE(true == obj.containsKey("hello"));
}
SECTION("supports std::string") {
REQUIRE(false == obj.containsKey("world"_s));
REQUIRE(true == obj.containsKey("hello"_s));
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == obj.containsKey(vla));
}
#endif
SECTION("supports JsonVariant") {
doc["key"] = "hello";
REQUIRE(true == obj.containsKey(obj["key"]));
REQUIRE(false == obj.containsKey(obj["hello"]));
}
}
TEST_CASE("JsonVariant::containsKey()") {
JsonDocument doc;
JsonVariant var = doc.to<JsonVariant>();
SECTION("returns false is unbound") {
CHECK_FALSE(JsonVariant().containsKey("hello"));
}
SECTION("containsKey(const char*)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("containsKey(std::string)") {
var["hello"] = "world";
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
SECTION("containsKey(JsonVariant)") {
var["hello"] = "world";
var["key"] = "hello";
REQUIRE(var.containsKey(doc["key"]) == true);
REQUIRE(var.containsKey(doc["foo"]) == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLAs") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
var["hello"] = "world";
REQUIRE(var.containsKey(vla) == true);
}
#endif
}
TEST_CASE("JsonVariantConst::containsKey()") {
JsonDocument doc;
doc["hello"] = "world";
JsonVariantConst var = doc.as<JsonVariant>();
SECTION("support const char*") {
REQUIRE(var.containsKey("hello") == true);
REQUIRE(var.containsKey("world") == false);
}
SECTION("support std::string") {
REQUIRE(var.containsKey("hello"_s) == true);
REQUIRE(var.containsKey("world"_s) == false);
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("supports VLA") {
size_t i = 16;
char vla[i];
strcpy(vla, "hello");
REQUIRE(true == var.containsKey(vla));
}
#endif
SECTION("support JsonVariant") {
doc["key"] = "hello";
REQUIRE(var.containsKey(var["key"]) == true);
REQUIRE(var.containsKey(var["foo"]) == false);
}
}

View File

@@ -0,0 +1,113 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include "Literals.hpp"
TEST_CASE("JsonDocument::createNestedArray()") {
JsonDocument doc;
SECTION("createNestedArray()") {
JsonArray array = doc.createNestedArray();
array.add(42);
REQUIRE(doc.as<std::string>() == "[[42]]");
}
SECTION("createNestedArray(const char*)") {
JsonArray array = doc.createNestedArray("key");
array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
}
SECTION("createNestedArray(std::string)") {
JsonArray array = doc.createNestedArray("key"_s);
array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("createNestedArray(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "key");
JsonArray array = doc.createNestedArray(vla);
array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
}
#endif
}
TEST_CASE("JsonArray::createNestedArray()") {
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
JsonArray nestedArray = array.createNestedArray();
nestedArray.add(42);
REQUIRE(doc.as<std::string>() == "[[42]]");
}
TEST_CASE("JsonObject::createNestedArray()") {
JsonDocument doc;
JsonObject object = doc.to<JsonObject>();
SECTION("createNestedArray(const char*)") {
JsonArray array = object.createNestedArray("key");
array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
}
SECTION("createNestedArray(std::string)") {
JsonArray array = object.createNestedArray("key"_s);
array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("createNestedArray(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "key");
JsonArray array = object.createNestedArray(vla);
array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
}
#endif
}
TEST_CASE("JsonVariant::createNestedArray()") {
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("createNestedArray()") {
JsonArray array = variant.createNestedArray();
array.add(42);
REQUIRE(doc.as<std::string>() == "[[42]]");
}
SECTION("createNestedArray(const char*)") {
JsonArray array = variant.createNestedArray("key");
array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
}
SECTION("createNestedArray(std::string)") {
JsonArray array = variant.createNestedArray("key"_s);
array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("createNestedArray(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "key");
JsonArray array = variant.createNestedArray(vla);
array.add(42);
REQUIRE(doc.as<std::string>() == "{\"key\":[42]}");
}
#endif
}

View File

@@ -0,0 +1,113 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include <string>
#include "Literals.hpp"
TEST_CASE("JsonDocument::createNestedObject()") {
JsonDocument doc;
SECTION("createNestedObject()") {
JsonObject object = doc.createNestedObject();
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "[{\"hello\":\"world\"}]");
}
SECTION("createNestedObject(const char*)") {
JsonObject object = doc.createNestedObject("key");
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
}
SECTION("createNestedObject(std::string)") {
JsonObject object = doc.createNestedObject("key"_s);
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("createNestedObject(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "key");
JsonObject object = doc.createNestedObject(vla);
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
}
#endif
}
TEST_CASE("JsonArray::createNestedObject()") {
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
JsonObject object = array.createNestedObject();
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "[{\"hello\":\"world\"}]");
}
TEST_CASE("JsonObject::createNestedObject()") {
JsonDocument doc;
JsonObject object = doc.to<JsonObject>();
SECTION("createNestedObject(const char*)") {
JsonObject nestedObject = object.createNestedObject("key");
nestedObject["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
}
SECTION("createNestedObject(std::string)") {
JsonObject nestedObject = object.createNestedObject("key"_s);
nestedObject["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("createNestedObject(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "key");
JsonObject nestedObject = object.createNestedObject(vla);
nestedObject["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
}
#endif
}
TEST_CASE("JsonVariant::createNestedObject()") {
JsonDocument doc;
JsonVariant variant = doc.to<JsonVariant>();
SECTION("createNestedObject()") {
JsonObject object = variant.createNestedObject();
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "[{\"hello\":\"world\"}]");
}
SECTION("createNestedObject(const char*)") {
JsonObject object = variant.createNestedObject("key");
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
}
SECTION("createNestedObject(std::string)") {
JsonObject object = variant.createNestedObject("key"_s);
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("createNestedObject(VLA)") {
size_t i = 16;
char vla[i];
strcpy(vla, "key");
JsonObject object = variant.createNestedObject(vla);
object["hello"] = "world";
REQUIRE(doc.as<std::string>() == "{\"key\":{\"hello\":\"world\"}}");
}
#endif
}

View File

@@ -0,0 +1,18 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JSON_ARRAY_SIZE") {
REQUIRE(JSON_ARRAY_SIZE(10) == ArduinoJson::detail::sizeofArray(10));
}
TEST_CASE("JSON_OBJECT_SIZE") {
REQUIRE(JSON_OBJECT_SIZE(10) == ArduinoJson::detail::sizeofObject(10));
}
TEST_CASE("JSON_STRING_SIZE") {
REQUIRE(JSON_STRING_SIZE(10) == 11); // issue #2054
}

View File

@@ -0,0 +1,51 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("JsonArray::memoryUsage()") {
JsonArray array;
REQUIRE(array.memoryUsage() == 0);
}
TEST_CASE("JsonArrayConst::memoryUsage()") {
JsonArrayConst array;
REQUIRE(array.memoryUsage() == 0);
}
TEST_CASE("JsonDocument::memoryUsage()") {
JsonDocument doc;
REQUIRE(doc.memoryUsage() == 0);
}
TEST_CASE("JsonObject::memoryUsage()") {
JsonObject array;
REQUIRE(array.memoryUsage() == 0);
}
TEST_CASE("JsonObjectConst::memoryUsage()") {
JsonObjectConst array;
REQUIRE(array.memoryUsage() == 0);
}
TEST_CASE("JsonVariant::memoryUsage()") {
JsonVariant doc;
REQUIRE(doc.memoryUsage() == 0);
}
TEST_CASE("JsonVariantConst::memoryUsage()") {
JsonVariantConst doc;
REQUIRE(doc.memoryUsage() == 0);
}
TEST_CASE("ElementProxy::memoryUsage()") {
JsonDocument doc;
REQUIRE(doc[0].memoryUsage() == 0);
}
TEST_CASE("MemberProxy::memoryUsage()") {
JsonDocument doc;
REQUIRE(doc["hello"].memoryUsage() == 0);
}

View File

@@ -0,0 +1,14 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("shallowCopy()") {
JsonDocument doc1, doc2;
doc1["b"] = "c";
doc2["a"].shallowCopy(doc1);
REQUIRE(doc2.as<std::string>() == "{\"a\":{\"b\":\"c\"}}");
}

View File

@@ -0,0 +1,32 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
macro(add_failing_build source_file)
get_filename_component(target ${source_file} NAME_WE)
add_executable(${target} ${source_file})
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
LABELS "WillFail"
)
endmacro()
add_failing_build(Issue978.cpp)
add_failing_build(read_long_long.cpp)
add_failing_build(write_long_long.cpp)
add_failing_build(variant_as_char.cpp)
add_failing_build(assign_char.cpp)
add_failing_build(deserialize_object.cpp)

View File

@@ -0,0 +1,13 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
struct Stream {};
int main() {
Stream* stream = 0;
JsonDocument doc;
deserializeJson(doc, stream);
}

View File

@@ -0,0 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
// See issue #1498
int main() {
JsonDocument doc;
doc["dummy"] = 'A';
}

View File

@@ -0,0 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
// See issue #2135
int main() {
JsonObject obj;
deserializeJson(obj, "");
}

View File

@@ -0,0 +1,16 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// 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
ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(long long)
int main() {
JsonDocument doc;
doc["dummy"].as<long long>();
}

View File

@@ -0,0 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
// See issue #1498
int main() {
JsonDocument doc;
doc["dummy"].as<char>();
}

View File

@@ -0,0 +1,15 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// 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
int main() {
JsonDocument doc;
doc["dummy"] = static_cast<long long>(42);
}

View File

@@ -0,0 +1,288 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#pragma once
#include <ArduinoJson/Memory/Allocator.hpp>
#include <ArduinoJson/Memory/MemoryPool.hpp>
#include <ArduinoJson/Memory/StringBuilder.hpp>
#include <sstream>
namespace {
struct FailingAllocator : ArduinoJson::Allocator {
static FailingAllocator* instance() {
static FailingAllocator allocator;
return &allocator;
}
private:
FailingAllocator() = default;
~FailingAllocator() = default;
void* allocate(size_t) override {
return nullptr;
}
void deallocate(void*) override {}
void* reallocate(void*, size_t) override {
return nullptr;
}
};
class AllocatorLogEntry {
public:
AllocatorLogEntry(std::string s, size_t n = 1) : str_(s), count_(n) {}
const std::string& str() const {
return str_;
}
size_t count() const {
return count_;
}
AllocatorLogEntry operator*(size_t n) const {
return AllocatorLogEntry(str_, n);
}
private:
std::string str_;
size_t count_;
};
inline AllocatorLogEntry Allocate(size_t s) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "allocate(%zu)", s);
return AllocatorLogEntry(buffer);
}
inline AllocatorLogEntry AllocateFail(size_t s) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "allocate(%zu) -> nullptr", s);
return AllocatorLogEntry(buffer);
}
inline AllocatorLogEntry Reallocate(size_t s1, size_t s2) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "reallocate(%zu, %zu)", s1, s2);
return AllocatorLogEntry(buffer);
}
inline AllocatorLogEntry ReallocateFail(size_t s1, size_t s2) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "reallocate(%zu, %zu) -> nullptr", s1, s2);
return AllocatorLogEntry(buffer);
}
inline AllocatorLogEntry Deallocate(size_t s) {
char buffer[32];
snprintf(buffer, sizeof(buffer), "deallocate(%zu)", s);
return AllocatorLogEntry(buffer);
}
class AllocatorLog {
public:
AllocatorLog() = default;
AllocatorLog(std::initializer_list<AllocatorLogEntry> list) {
for (auto& entry : list)
append(entry);
}
void clear() {
log_.str("");
}
void append(const AllocatorLogEntry& entry) {
for (size_t i = 0; i < entry.count(); i++)
log_ << entry.str() << "\n";
}
std::string str() const {
auto s = log_.str();
if (s.empty())
return "(empty)";
s.pop_back(); // remove the trailing '\n'
return s;
}
bool operator==(const AllocatorLog& other) const {
return str() == other.str();
}
friend std::ostream& operator<<(std::ostream& os, const AllocatorLog& log) {
os << log.str();
return os;
}
private:
std::ostringstream log_;
};
class SpyingAllocator : public ArduinoJson::Allocator {
public:
SpyingAllocator(
Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance())
: upstream_(upstream) {}
virtual ~SpyingAllocator() {}
size_t allocatedBytes() const {
return allocatedBytes_;
}
void* allocate(size_t n) override {
auto block = reinterpret_cast<AllocatedBlock*>(
upstream_->allocate(sizeof(AllocatedBlock) + n - 1));
if (block) {
log_.append(Allocate(n));
allocatedBytes_ += n;
block->size = n;
return block->payload;
} else {
log_.append(AllocateFail(n));
return nullptr;
}
}
void deallocate(void* p) override {
auto block = AllocatedBlock::fromPayload(p);
allocatedBytes_ -= block->size;
log_.append(Deallocate(block ? block->size : 0));
upstream_->deallocate(block);
}
void* reallocate(void* p, size_t n) override {
auto block = AllocatedBlock::fromPayload(p);
auto oldSize = block ? block->size : 0;
block = reinterpret_cast<AllocatedBlock*>(
upstream_->reallocate(block, sizeof(AllocatedBlock) + n - 1));
if (block) {
log_.append(Reallocate(oldSize, n));
block->size = n;
allocatedBytes_ += n - oldSize;
return block->payload;
} else {
log_.append(ReallocateFail(oldSize, n));
return nullptr;
}
}
void clearLog() {
log_.clear();
}
const AllocatorLog& log() const {
return log_;
}
private:
struct AllocatedBlock {
size_t size;
char payload[1];
static AllocatedBlock* fromPayload(void* p) {
if (!p)
return nullptr;
return reinterpret_cast<AllocatedBlock*>(
// Cast to void* to silence "cast increases required alignment of
// target type [-Werror=cast-align]"
reinterpret_cast<void*>(reinterpret_cast<char*>(p) -
offsetof(AllocatedBlock, payload)));
}
};
AllocatorLog log_;
Allocator* upstream_;
size_t allocatedBytes_ = 0;
};
class KillswitchAllocator : public ArduinoJson::Allocator {
public:
KillswitchAllocator(
Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance())
: working_(true), upstream_(upstream) {}
virtual ~KillswitchAllocator() {}
void* allocate(size_t n) override {
return working_ ? upstream_->allocate(n) : 0;
}
void deallocate(void* p) override {
upstream_->deallocate(p);
}
void* reallocate(void* ptr, size_t n) override {
return working_ ? upstream_->reallocate(ptr, n) : 0;
}
// Turn the killswitch on, so all allocation fail
void on() {
working_ = false;
}
private:
bool working_;
Allocator* upstream_;
};
class TimebombAllocator : public ArduinoJson::Allocator {
public:
TimebombAllocator(
size_t initialCountdown,
Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance())
: countdown_(initialCountdown), upstream_(upstream) {}
virtual ~TimebombAllocator() {}
void* allocate(size_t n) override {
if (!countdown_)
return nullptr;
countdown_--;
return upstream_->allocate(n);
}
void deallocate(void* p) override {
upstream_->deallocate(p);
}
void* reallocate(void* ptr, size_t n) override {
if (!countdown_)
return nullptr;
countdown_--;
return upstream_->reallocate(ptr, n);
}
void setCountdown(size_t value) {
countdown_ = value;
}
private:
size_t countdown_ = 0;
Allocator* upstream_;
};
} // namespace
inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) {
using namespace ArduinoJson::detail;
return sizeof(MemoryPool<VariantData>) * n;
}
inline size_t sizeofPool(
ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) {
using namespace ArduinoJson::detail;
return MemoryPool<VariantData>::slotsToBytes(n);
}
inline size_t sizeofStringBuffer(size_t iteration = 1) {
// returns 31, 63, 127, 255, etc.
auto capacity = ArduinoJson::detail::StringBuilder::initialCapacity;
for (size_t i = 1; i < iteration; i++)
capacity = capacity * 2 + 1;
return ArduinoJson::detail::sizeofString(capacity);
}
inline size_t sizeofString(const char* s) {
return ArduinoJson::detail::sizeofString(strlen(s));
}

View File

@@ -0,0 +1,13 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#pragma once
#include "api/Print.h"
#include "api/Stream.h"
#include "api/String.h"
#include "avr/pgmspace.h"
#define ARDUINO
#define ARDUINO_H_INCLUDED 1

View File

@@ -0,0 +1,24 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#pragma once
#include <sstream>
class CustomReader {
std::stringstream stream_;
public:
CustomReader(const char* input) : stream_(input) {}
CustomReader(const CustomReader&) = delete;
int read() {
return stream_.get();
}
size_t readBytes(char* buffer, size_t length) {
stream_.read(buffer, static_cast<std::streamsize>(length));
return static_cast<size_t>(stream_.gcount());
}
};

View File

@@ -0,0 +1,12 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#pragma once
#include <string>
// the space before _s is required by GCC 4.8
inline std::string operator"" _s(const char* str, size_t len) {
return std::string(str, len);
}

View File

@@ -0,0 +1,33 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
class Print {
public:
virtual ~Print() {}
virtual size_t write(uint8_t) = 0;
virtual size_t write(const uint8_t* buffer, size_t size) = 0;
size_t write(const char* str) {
if (!str)
return 0;
return write(reinterpret_cast<const uint8_t*>(str), strlen(str));
}
size_t write(const char* buffer, size_t size) {
return write(reinterpret_cast<const uint8_t*>(buffer), size);
}
};
class Printable {
public:
virtual ~Printable() {}
virtual size_t printTo(Print& p) const = 0;
};

View File

@@ -0,0 +1,14 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#pragma once
// Reproduces Arduino's Stream class
class Stream // : public Print
{
public:
virtual ~Stream() {}
virtual int read() = 0;
virtual size_t readBytes(char* buffer, size_t length) = 0;
};

View File

@@ -0,0 +1,75 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#pragma once
#include <string>
// Reproduces Arduino's String class
class String {
public:
String() = default;
String(const char* s) {
if (s)
str_.assign(s);
}
void limitCapacityTo(size_t maxCapacity) {
maxCapacity_ = maxCapacity;
}
unsigned char concat(const char* s) {
return concat(s, strlen(s));
}
size_t length() const {
return str_.size();
}
const char* c_str() const {
return str_.c_str();
}
bool operator==(const char* s) const {
return str_ == s;
}
String& operator=(const char* s) {
if (s)
str_.assign(s);
else
str_.clear();
return *this;
}
char operator[](unsigned int index) const {
if (index >= str_.size())
return 0;
return str_[index];
}
friend std::ostream& operator<<(std::ostream& lhs, const ::String& rhs) {
lhs << rhs.str_;
return lhs;
}
protected:
// This function is protected in most Arduino cores
unsigned char concat(const char* s, size_t n) {
if (str_.size() + n > maxCapacity_)
return 0;
str_.append(s, n);
return 1;
}
private:
std::string str_;
size_t maxCapacity_ = 1024;
};
class StringSumHelper : public ::String {};
inline bool operator==(const std::string& lhs, const ::String& rhs) {
return lhs == rhs.c_str();
}

View File

@@ -0,0 +1,31 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#pragma once
#include <stdint.h> // uint8_t
#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));
}
#define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \
static type const ARDUINOJSON_CONCAT2(name, _progmem)[] = __VA_ARGS__; \
static type const* name = reinterpret_cast<type const*>( \
convertPtrToFlash(ARDUINOJSON_CONCAT2(name, _progmem)));

View File

@@ -0,0 +1,24 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
add_executable(IntegrationTests
gbathree.cpp
issue772.cpp
round_trip.cpp
openweathermap.cpp
)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6)
target_compile_options(IntegrationTests
PUBLIC
-fsingle-precision-constant # issue 544
)
endif()
add_test(IntegrationTests IntegrationTests)
set_tests_properties(IntegrationTests
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,210 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
TEST_CASE("Gbathree") {
JsonDocument doc;
DeserializationError error = deserializeJson(
doc,
"{\"protocol_name\":\"fluorescence\",\"repeats\":1,\"wait\":0,"
"\"averages\":1,\"measurements\":3,\"meas2_light\":15,\"meas1_"
"baseline\":0,\"act_light\":20,\"pulsesize\":25,\"pulsedistance\":"
"10000,\"actintensity1\":50,\"actintensity2\":255,\"measintensity\":"
"255,\"calintensity\":255,\"pulses\":[50,50,50],\"act\":[2,1,2,2],"
"\"red\":[2,2,2,2],\"detectors\":[[34,34,34,34],[34,34,34,34],[34,"
"34,34,34],[34,34,34,34]],\"alta\":[2,2,2,2],\"altb\":[2,2,2,2],"
"\"measlights\":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,"
"15,15]],\"measlights2\":[[15,15,15,15],[15,15,15,15],[15,15,15,15],"
"[15,15,15,15]],\"altc\":[2,2,2,2],\"altd\":[2,2,2,2]}");
JsonObject root = doc.as<JsonObject>();
SECTION("Success") {
REQUIRE(error == DeserializationError::Ok);
}
SECTION("ProtocolName") {
REQUIRE("fluorescence" == root["protocol_name"]);
}
SECTION("Repeats") {
REQUIRE(1 == root["repeats"]);
}
SECTION("Wait") {
REQUIRE(0 == root["wait"]);
}
SECTION("Measurements") {
REQUIRE(3 == root["measurements"]);
}
SECTION("Meas2_Light") {
REQUIRE(15 == root["meas2_light"]);
}
SECTION("Meas1_Baseline") {
REQUIRE(0 == root["meas1_baseline"]);
}
SECTION("Act_Light") {
REQUIRE(20 == root["act_light"]);
}
SECTION("Pulsesize") {
REQUIRE(25 == root["pulsesize"]);
}
SECTION("Pulsedistance") {
REQUIRE(10000 == root["pulsedistance"]);
}
SECTION("Actintensity1") {
REQUIRE(50 == root["actintensity1"]);
}
SECTION("Actintensity2") {
REQUIRE(255 == root["actintensity2"]);
}
SECTION("Measintensity") {
REQUIRE(255 == root["measintensity"]);
}
SECTION("Calintensity") {
REQUIRE(255 == root["calintensity"]);
}
SECTION("Pulses") {
// "pulses":[50,50,50]
JsonArray array = root["pulses"];
REQUIRE(array.isNull() == false);
REQUIRE(3 == array.size());
for (size_t i = 0; i < 3; i++) {
REQUIRE(50 == array[i]);
}
}
SECTION("Act") {
// "act":[2,1,2,2]
JsonArray array = root["act"];
REQUIRE(array.isNull() == false);
REQUIRE(4 == array.size());
REQUIRE(2 == array[0]);
REQUIRE(1 == array[1]);
REQUIRE(2 == array[2]);
REQUIRE(2 == array[3]);
}
SECTION("Detectors") {
// "detectors":[[34,34,34,34],[34,34,34,34],[34,34,34,34],[34,34,34,34]]
JsonArray array = root["detectors"];
REQUIRE(array.isNull() == false);
REQUIRE(4 == array.size());
for (size_t i = 0; i < 4; i++) {
JsonArray nestedArray = array[i];
REQUIRE(4 == nestedArray.size());
for (size_t j = 0; j < 4; j++) {
REQUIRE(34 == nestedArray[j]);
}
}
}
SECTION("Alta") {
// alta:[2,2,2,2]
JsonArray array = root["alta"];
REQUIRE(array.isNull() == false);
REQUIRE(4 == array.size());
for (size_t i = 0; i < 4; i++) {
REQUIRE(2 == array[i]);
}
}
SECTION("Altb") {
// altb:[2,2,2,2]
JsonArray array = root["altb"];
REQUIRE(array.isNull() == false);
REQUIRE(4 == array.size());
for (size_t i = 0; i < 4; i++) {
REQUIRE(2 == array[i]);
}
}
SECTION("Measlights") {
// "measlights":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,15,15]]
JsonArray array = root["measlights"];
REQUIRE(array.isNull() == false);
REQUIRE(4 == array.size());
for (size_t i = 0; i < 4; i++) {
JsonArray nestedArray = array[i];
REQUIRE(4 == nestedArray.size());
for (size_t j = 0; j < 4; j++) {
REQUIRE(15 == nestedArray[j]);
}
}
}
SECTION("Measlights2") {
// "measlights2":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,15,15]]
JsonArray array = root["measlights2"];
REQUIRE(array.isNull() == false);
REQUIRE(4 == array.size());
for (size_t i = 0; i < 4; i++) {
JsonArray nestedArray = array[i];
REQUIRE(4 == nestedArray.size());
for (size_t j = 0; j < 4; j++) {
REQUIRE(15 == nestedArray[j]);
}
}
}
SECTION("Altc") {
// altc:[2,2,2,2]
JsonArray array = root["altc"];
REQUIRE(array.isNull() == false);
REQUIRE(4 == array.size());
for (size_t i = 0; i < 4; i++) {
REQUIRE(2 == array[i]);
}
}
SECTION("Altd") {
// altd:[2,2,2,2]
JsonArray array = root["altd"];
REQUIRE(array.isNull() == false);
REQUIRE(4 == array.size());
for (size_t i = 0; i < 4; i++) {
REQUIRE(2 == array[i]);
}
}
}

View File

@@ -0,0 +1,28 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
// https://github.com/bblanchon/ArduinoJson/issues/772
TEST_CASE("Issue772") {
JsonDocument doc1;
JsonDocument doc2;
DeserializationError err;
std::string data =
"{\"state\":{\"reported\":{\"timestamp\":\"2018-07-02T09:40:12Z\","
"\"mac\":\"2C3AE84FC076\",\"firmwareVersion\":\"v0.2.7-5-gf4d4d78\","
"\"visibleLight\":261,\"infraRed\":255,\"ultraViolet\":0.02,"
"\"Temperature\":26.63,\"Pressure\":101145.7,\"Humidity\":54.79883,"
"\"Vbat\":4.171261,\"soilMoisture\":0,\"ActB\":0}}}";
err = deserializeJson(doc1, data);
REQUIRE(err == DeserializationError::Ok);
data = "";
serializeMsgPack(doc1, data);
err = deserializeMsgPack(doc2, data);
REQUIRE(err == DeserializationError::Ok);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,82 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
void check(std::string originalJson) {
JsonDocument doc;
std::string prettyJson;
deserializeJson(doc, originalJson);
serializeJsonPretty(doc, prettyJson);
std::string finalJson;
deserializeJson(doc, originalJson);
serializeJson(doc, finalJson);
REQUIRE(originalJson == finalJson);
}
TEST_CASE("Round Trip: parse -> prettyPrint -> parse -> print") {
SECTION("OpenWeatherMap") {
check(
"{\"coord\":{\"lon\":145.77,\"lat\":-16.92},\"sys\":{\"type\":1,\"id\":"
"8166,\"message\":0.1222,\"country\":\"AU\",\"sunrise\":1414784325,"
"\"sunset\":1414830137},\"weather\":[{\"id\":801,\"main\":\"Clouds\","
"\"description\":\"few clouds\",\"icon\":\"02n\"}],\"base\":\"cmc "
"stations\",\"main\":{\"temp\":296.15,\"pressure\":1014,\"humidity\":"
"83,\"temp_min\":296.15,\"temp_max\":296.15},\"wind\":{\"speed\":2.22,"
"\"deg\":114.501},\"clouds\":{\"all\":20},\"dt\":1414846800,\"id\":"
"2172797,\"name\":\"Cairns\",\"cod\":200}");
}
SECTION("YahooQueryLanguage") {
check(
"{\"query\":{\"count\":40,\"created\":\"2014-11-01T14:16:49Z\","
"\"lang\":\"fr-FR\",\"results\":{\"item\":[{\"title\":\"Burkina army "
"backs Zida as interim leader\"},{\"title\":\"British jets intercept "
"Russian bombers\"},{\"title\":\"Doubts chip away at nation's most "
"trusted agencies\"},{\"title\":\"Cruise ship stuck off Norway, no "
"damage\"},{\"title\":\"U.S. military launches 10 air strikes in "
"Syria, Iraq\"},{\"title\":\"Blackout hits Bangladesh as line from "
"India fails\"},{\"title\":\"Burkina Faso president in Ivory Coast "
"after ouster\"},{\"title\":\"Kurds in Turkey rally to back city "
"besieged by IS\"},{\"title\":\"A majority of Scots would vote for "
"independence now:poll\"},{\"title\":\"Tunisia elections possible "
"model for region\"},{\"title\":\"Islamic State kills 85 more members "
"of Iraqi tribe\"},{\"title\":\"Iraqi officials:IS extremists line "
"up, kill 50\"},{\"title\":\"Burkina Faso army backs presidential "
"guard official to lead transition\"},{\"title\":\"Kurdish peshmerga "
"arrive with weapons in Syria's Kobani\"},{\"title\":\"Driver sought "
"in crash that killed 3 on Halloween\"},{\"title\":\"Ex-Marine arrives "
"in US after release from Mexico jail\"},{\"title\":\"UN panel "
"scrambling to finish climate report\"},{\"title\":\"Investigators, "
"Branson go to spacecraft crash site\"},{\"title\":\"Soldiers vie for "
"power after Burkina Faso president quits\"},{\"title\":\"For a man "
"without a party, turnout is big test\"},{\"title\":\"'We just had a "
"hunch':US marshals nab Eric Frein\"},{\"title\":\"Boko Haram leader "
"threatens to kill German hostage\"},{\"title\":\"Nurse free to move "
"about as restrictions eased\"},{\"title\":\"Former Burkina president "
"Compaore arrives in Ivory Coast:sources\"},{\"title\":\"Libyan port "
"rebel leader refuses to hand over oil ports to rival "
"group\"},{\"title\":\"Iraqi peshmerga fighters prepare for Syria "
"battle\"},{\"title\":\"1 Dem Senate candidate welcoming Obama's "
"help\"},{\"title\":\"Bikers cancel party after police recover "
"bar\"},{\"title\":\"New question in Texas:Can Davis survive "
"defeat?\"},{\"title\":\"Ukraine rebels to hold election, despite "
"criticism\"},{\"title\":\"Iraqi officials say Islamic State group "
"lines up, kills 50 tribesmen, women in Anbar "
"province\"},{\"title\":\"James rebounds, leads Cavaliers past "
"Bulls\"},{\"title\":\"UK warns travelers they could be terror "
"targets\"},{\"title\":\"Hello Kitty celebrates 40th "
"birthday\"},{\"title\":\"A look at people killed during space "
"missions\"},{\"title\":\"Nigeria's purported Boko Haram leader says "
"has 'married off' girls:AFP\"},{\"title\":\"Mexico orders immediate "
"release of Marine veteran\"},{\"title\":\"As election closes in, "
"Obama on center stage\"},{\"title\":\"Body of Zambian president "
"arrives home\"},{\"title\":\"South Africa arrests 2 Vietnamese for "
"poaching\"}]}}}");
}
}

View File

@@ -0,0 +1,25 @@
# ArduinoJson - https://arduinojson.org
# Copyright © 2014-2025, Benoit BLANCHON
# MIT License
add_executable(JsonArrayTests
add.cpp
clear.cpp
compare.cpp
copyArray.cpp
equals.cpp
isNull.cpp
iterator.cpp
nesting.cpp
remove.cpp
size.cpp
subscript.cpp
unbound.cpp
)
add_test(JsonArray JsonArrayTests)
set_tests_properties(JsonArray
PROPERTIES
LABELS "Catch"
)

View File

@@ -0,0 +1,262 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
#include "Literals.hpp"
using ArduinoJson::detail::sizeofArray;
TEST_CASE("JsonArray::add(T)") {
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonArray array = doc.to<JsonArray>();
SECTION("int") {
array.add(123);
REQUIRE(123 == array[0].as<int>());
REQUIRE(array[0].is<int>());
REQUIRE(array[0].is<double>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("double") {
array.add(123.45);
REQUIRE(123.45 == array[0].as<double>());
REQUIRE(array[0].is<double>());
REQUIRE_FALSE(array[0].is<bool>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("bool") {
array.add(true);
REQUIRE(array[0].as<bool>() == true);
REQUIRE(array[0].is<bool>());
REQUIRE_FALSE(array[0].is<int>());
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("string literal") {
array.add("hello");
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(array[0].is<const char*>());
REQUIRE(array[0].is<int>() == false);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
SECTION("std::string") {
array.add("hello"_s);
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(array[0].is<const char*>() == true);
REQUIRE(array[0].is<int>() == false);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("const char*") {
const char* str = "hello";
array.add(str);
REQUIRE(array[0].as<std::string>() == "hello");
REQUIRE(array[0].as<const char*>() != str);
REQUIRE(array[0].is<const char*>() == true);
REQUIRE(array[0].is<int>() == false);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
SECTION("serialized(const char*)") {
array.add(serialized("{}"));
REQUIRE(doc.as<std::string>() == "[{}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("serialized(char*)") {
array.add(serialized(const_cast<char*>("{}")));
REQUIRE(doc.as<std::string>() == "[{}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("serialized(std::string)") {
array.add(serialized("{}"_s));
REQUIRE(doc.as<std::string>() == "[{}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("{}")),
});
}
SECTION("serialized(std::string)") {
array.add(serialized("\0XX"_s));
REQUIRE(doc.as<std::string>() == "[\0XX]"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString(" XX")),
});
}
#ifdef HAS_VARIABLE_LENGTH_ARRAY
SECTION("vla") {
size_t i = 16;
char vla[i];
strcpy(vla, "world");
array.add(vla);
strcpy(vla, "hello");
REQUIRE(array[0] == "world"_s);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("hello")),
});
}
#endif
SECTION("nested array") {
JsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();
array.add(arr);
REQUIRE(arr == array[0].as<JsonArray>());
REQUIRE(array[0].is<JsonArray>());
REQUIRE_FALSE(array[0].is<int>());
}
SECTION("nested object") {
JsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
array.add(obj);
REQUIRE(obj == array[0].as<JsonObject>());
REQUIRE(array[0].is<JsonObject>());
REQUIRE_FALSE(array[0].is<int>());
}
SECTION("array subscript") {
const char* str = "hello";
JsonDocument doc2;
JsonArray arr = doc2.to<JsonArray>();
arr.add(str);
array.add(arr[0]);
REQUIRE(str == array[0]);
}
SECTION("object subscript") {
const char* str = "hello";
JsonDocument doc2;
JsonObject obj = doc2.to<JsonObject>();
obj["x"] = str;
array.add(obj["x"]);
REQUIRE(str == array[0]);
}
}
TEST_CASE("JsonArray::add<T>()") {
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
SECTION("add<JsonArray>()") {
JsonArray nestedArray = array.add<JsonArray>();
nestedArray.add(1);
nestedArray.add(2);
REQUIRE(doc.as<std::string>() == "[[1,2]]");
}
SECTION("add<JsonObject>()") {
JsonObject nestedObject = array.add<JsonObject>();
nestedObject["a"] = 1;
nestedObject["b"] = 2;
REQUIRE(doc.as<std::string>() == "[{\"a\":1,\"b\":2}]");
}
SECTION("add<JsonVariant>()") {
JsonVariant nestedVariant = array.add<JsonVariant>();
nestedVariant.set(42);
REQUIRE(doc.as<std::string>() == "[42]");
}
}
TEST_CASE("JsonObject::add(JsonObject) ") {
JsonDocument doc1;
doc1["key1"_s] = "value1"_s;
TimebombAllocator allocator(10);
SpyingAllocator spy(&allocator);
JsonDocument doc2(&spy);
JsonArray array = doc2.to<JsonArray>();
SECTION("success") {
bool result = array.add(doc1.as<JsonObject>());
REQUIRE(result == true);
REQUIRE(doc2.as<std::string>() == "[{\"key1\":\"value1\"}]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("key1")),
Allocate(sizeofString("value1")),
});
}
SECTION("partial failure") { // issue #2081
allocator.setCountdown(2);
bool result = array.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
Allocate(sizeofString("key1")),
AllocateFail(sizeofString("value1")),
Deallocate(sizeofString("key1")),
});
}
SECTION("complete failure") {
allocator.setCountdown(0);
bool result = array.add(doc1.as<JsonObject>());
REQUIRE(result == false);
REQUIRE(doc2.as<std::string>() == "[]");
REQUIRE(spy.log() == AllocatorLog{
AllocateFail(sizeofPool()),
});
}
}

View File

@@ -0,0 +1,46 @@
// ArduinoJson - https://arduinojson.org
// Copyright © 2014-2025, Benoit BLANCHON
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
#include "Allocators.hpp"
TEST_CASE("JsonArray::clear()") {
SECTION("No-op on null JsonArray") {
JsonArray array;
array.clear();
REQUIRE(array.isNull() == true);
REQUIRE(array.size() == 0);
}
SECTION("Removes all elements") {
JsonDocument doc;
JsonArray array = doc.to<JsonArray>();
array.add(1);
array.add(2);
array.clear();
REQUIRE(array.size() == 0);
REQUIRE(array.isNull() == false);
}
SECTION("Removed elements are recycled") {
SpyingAllocator spy;
JsonDocument doc(&spy);
JsonArray array = doc.to<JsonArray>();
// fill the pool entirely
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
array.add(i);
// clear and fill again
array.clear();
for (int i = 0; i < ARDUINOJSON_POOL_CAPACITY; i++)
array.add(i);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
}
}

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