Test extension slot allocation failure

This commit is contained in:
Benoit Blanchon
2024-08-27 14:51:22 +02:00
parent 0278e94fce
commit ee02c0d573
4 changed files with 70 additions and 37 deletions

View File

@ -188,12 +188,12 @@ TEST_CASE("JsonVariant::set() when there is enough memory") {
} }
SECTION("int64_t") { SECTION("int64_t") {
bool result = variant.set(int64_t(-2147483649)); bool result = variant.set(int64_t(-2147483649LL));
doc.shrinkToFit(); doc.shrinkToFit();
REQUIRE(result == true); REQUIRE(result == true);
REQUIRE(variant.is<int64_t>() == true); REQUIRE(variant.is<int64_t>() == true);
REQUIRE(variant.as<int64_t>() == -2147483649); REQUIRE(variant.as<int64_t>() == -2147483649LL);
REQUIRE(spy.log() == REQUIRE(spy.log() ==
AllocatorLog{ AllocatorLog{
Allocate(sizeofPool()), Allocate(sizeofPool()),
@ -278,6 +278,34 @@ TEST_CASE("JsonVariant::set() with not enough memory") {
REQUIRE(result == false); REQUIRE(result == false);
REQUIRE(v.isNull()); REQUIRE(v.isNull());
} }
SECTION("int32_t") {
bool result = v.set(-42);
REQUIRE(result == true);
REQUIRE(v.is<int32_t>());
}
SECTION("int64_t") {
bool result = v.set(-2147483649LL);
REQUIRE(result == false);
REQUIRE(v.isNull());
}
SECTION("uint32_t") {
bool result = v.set(42);
REQUIRE(result == true);
REQUIRE(v.is<uint32_t>());
}
SECTION("uint64_t") {
bool result = v.set(4294967296U);
REQUIRE(result == false);
REQUIRE(v.isNull());
}
} }
TEST_CASE("JsonVariant::set() releases the previous value") { TEST_CASE("JsonVariant::set() releases the previous value") {
@ -325,29 +353,33 @@ TEST_CASE("JsonVariant::set() releases the previous value") {
} }
} }
TEST_CASE("Extension slots") { TEST_CASE("JsonVariant::set() reuses extension slot") {
SpyingAllocator spy; SpyingAllocator spy;
JsonDocument doc(&spy); JsonDocument doc(&spy);
JsonVariant variant = doc.to<JsonVariant>();
SECTION("double requires two slot") { variant.set(1.2);
int i = ARDUINOJSON_POOL_CAPACITY - 1; doc.shrinkToFit();
spy.clearLog();
// make sure the pool is full SECTION("double") {
doc[i] = 1; bool result = variant.set(3.4);
REQUIRE(spy.log() == AllocatorLog{
Allocate(sizeofPool()),
});
// replace with a float => no allocation REQUIRE(result == true);
spy.clearLog();
doc[i] = 1.2f;
REQUIRE(spy.log() == AllocatorLog{}); REQUIRE(spy.log() == AllocatorLog{});
}
// replace with a double => new pool needed SECTION("int64_t") {
spy.clearLog(); bool result = variant.set(-2147483649LL);
doc[i] = 1.2;
REQUIRE(spy.log() == AllocatorLog{ REQUIRE(result == true);
Allocate(sizeofPool()), REQUIRE(spy.log() == AllocatorLog{});
}); }
SECTION("uint64_t") {
bool result = variant.set(4294967296U);
REQUIRE(result == true);
REQUIRE(spy.log() == AllocatorLog{});
} }
} }

View File

@ -65,8 +65,7 @@ struct Converter<T, detail::enable_if_t<detail::is_integral<T>::value &&
return false; return false;
auto resources = getResourceManager(dst); auto resources = getResourceManager(dst);
data->clear(resources); data->clear(resources);
data->setInteger(src, resources); return data->setInteger(src, resources);
return true;
} }
static T fromJson(JsonVariantConst src) { static T fromJson(JsonVariantConst src) {

View File

@ -427,12 +427,12 @@ class VariantData {
enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*); enable_if_t<sizeof(T) == 8, bool> setFloat(T value, ResourceManager*);
template <typename T> template <typename T>
enable_if_t<is_signed<T>::value> setInteger(T value, enable_if_t<is_signed<T>::value, bool> setInteger(T value,
ResourceManager* resources); ResourceManager* resources);
template <typename T> template <typename T>
enable_if_t<is_unsigned<T>::value> setInteger(T value, enable_if_t<is_unsigned<T>::value, bool> setInteger(
ResourceManager* resources); T value, ResourceManager* resources);
void setRawString(StringNode* s) { void setRawString(StringNode* s) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first

View File

@ -92,7 +92,7 @@ enable_if_t<sizeof(T) == 8, bool> VariantData::setFloat(
} }
template <typename T> template <typename T>
enable_if_t<is_signed<T>::value> VariantData::setInteger( enable_if_t<is_signed<T>::value, bool> VariantData::setInteger(
T value, ResourceManager* resources) { T value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first
(void)resources; // silence warning (void)resources; // silence warning
@ -104,17 +104,18 @@ enable_if_t<is_signed<T>::value> VariantData::setInteger(
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
else { else {
auto extension = resources->allocExtension(); auto extension = resources->allocExtension();
if (extension) { if (!extension)
type_ = VALUE_IS_INT64; return false;
content_.asSlotId = extension.id(); type_ = VALUE_IS_INT64;
extension->asInt64 = value; content_.asSlotId = extension.id();
} extension->asInt64 = value;
} }
#endif #endif
return true;
} }
template <typename T> template <typename T>
enable_if_t<is_unsigned<T>::value> VariantData::setInteger( enable_if_t<is_unsigned<T>::value, bool> VariantData::setInteger(
T value, ResourceManager* resources) { T value, ResourceManager* resources) {
ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first ARDUINOJSON_ASSERT(type_ == VALUE_IS_NULL); // must call clear() first
(void)resources; // silence warning (void)resources; // silence warning
@ -126,13 +127,14 @@ enable_if_t<is_unsigned<T>::value> VariantData::setInteger(
#if ARDUINOJSON_USE_LONG_LONG #if ARDUINOJSON_USE_LONG_LONG
else { else {
auto extension = resources->allocExtension(); auto extension = resources->allocExtension();
if (extension) { if (!extension)
type_ = VALUE_IS_UINT64; return false;
content_.asSlotId = extension.id(); type_ = VALUE_IS_UINT64;
extension->asUint64 = value; content_.asSlotId = extension.id();
} extension->asUint64 = value;
} }
#endif #endif
return true;
} }
ARDUINOJSON_END_PRIVATE_NAMESPACE ARDUINOJSON_END_PRIVATE_NAMESPACE