Compare commits

..

9 Commits
v4.2 ... v4.5

26 changed files with 539 additions and 126 deletions

View File

@ -9,6 +9,7 @@
// This file is here to help the Arduino IDE find the other files.
#include "src/Arduino/Print.cpp"
#include "src/DynamicJsonBuffer.cpp"
#include "src/Internals/IndentedPrint.cpp"
#include "src/Internals/JsonParser.cpp"
#include "src/Internals/List.cpp"

View File

@ -1,6 +1,25 @@
Arduino JSON: change log
========================
v4.5
----
* Fixed buffer overflow when input contains a backslash followed by a terminator (issue #81)
**Upgrading is recommended** since previous versions contain a potential security risk.
v4.4
----
* Added `JsonArray::measureLength()` and `JsonObject::measureLength()` (issue #75)
v4.3
----
* Added `JsonArray::removeAt()` to remove an element of an array (issue #58)
* Fixed stack-overflow in `DynamicJsonBuffer` when parsing huge JSON files (issue #65)
* Fixed wrong return value of `parseArray()` and `parseObject()` when allocation fails (issue #68)
v4.2
----

View File

@ -0,0 +1,55 @@
// Send a JSON object on UDP at regular interval
//
// You can easily test this program with netcat:
// $ nc -ulp 8888
//
// by Benoit Blanchon, MIT License 2015
#include <SPI.h>
#include <Ethernet.h>
#include <ArduinoJson.h>
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress localIp(192, 168, 0, 177);
IPAddress remoteIp(192, 168, 0, 109);
unsigned int remotePort = 8888;
unsigned localPort = 8888;
EthernetUDP udp;
JsonObject& buildJson(JsonBuffer& jsonBuffer) {
JsonObject& root = jsonBuffer.createObject();
JsonArray& analogValues = root.createNestedArray("analog");
for (int pin = 0; pin < 6; pin++) {
int value = analogRead(pin);
analogValues.add(value);
}
JsonArray& digitalValues = root.createNestedArray("digital");
for (int pin = 0; pin < 14; pin++) {
int value = digitalRead(pin);
digitalValues.add(value);
}
return root;
}
void sendJson(JsonObject& json) {
udp.beginPacket(remoteIp, remotePort);
json.printTo(udp);
udp.println();
udp.endPacket();
}
void setup() {
Ethernet.begin(mac, localIp);
udp.begin(localPort);
}
void loop() {
delay(1000);
StaticJsonBuffer<300> jsonBuffer;
JsonObject& json = buildJson(jsonBuffer);
sendJson(json);
}

View File

@ -8,60 +8,39 @@
#include "JsonBuffer.hpp"
#include <stdlib.h>
namespace ArduinoJson {
// Forward declaration
namespace Internals {
struct DynamicJsonBufferBlock;
}
// Implements a JsonBuffer with dynamic memory allocation.
// You are strongly encouraged to consider using StaticJsonBuffer which is much
// more suitable for embedded systems.
class DynamicJsonBuffer : public JsonBuffer {
public:
DynamicJsonBuffer() : _next(NULL), _size(0) {}
DynamicJsonBuffer();
~DynamicJsonBuffer();
~DynamicJsonBuffer() { delete _next; }
size_t size() const { return _size + (_next ? _next->size() : 0); }
size_t blockCount() const { return 1 + (_next ? _next->blockCount() : 0); }
static const size_t BLOCK_CAPACITY = 32;
size_t size() const;
protected:
virtual void* alloc(size_t bytes) {
if (canAllocInThisBlock(bytes))
return allocInThisBlock(bytes);
else if (canAllocInOtherBlocks(bytes))
return allocInOtherBlocks(bytes);
else
return NULL;
}
virtual void* alloc(size_t bytes);
private:
bool canAllocInThisBlock(size_t bytes) const {
return _size + bytes <= BLOCK_CAPACITY;
}
typedef Internals::DynamicJsonBufferBlock Block;
void* allocInThisBlock(size_t bytes) {
void* p = _buffer + _size;
_size += bytes;
return p;
}
static const size_t FIRST_BLOCK_CAPACITY = 32;
bool canAllocInOtherBlocks(size_t bytes) const {
// by design a DynamicJsonBuffer can't alloc a block bigger than
// BLOCK_CAPACITY
return bytes <= BLOCK_CAPACITY;
}
static Block* createBlock(size_t capacity);
void* allocInOtherBlocks(size_t bytes) {
if (!_next) {
_next = new DynamicJsonBuffer();
if (!_next) return NULL;
}
return _next->alloc(bytes);
}
inline bool canAllocInHead(size_t bytes) const;
inline void* allocInHead(size_t bytes);
inline void addNewBlock();
DynamicJsonBuffer* _next;
size_t _size;
uint8_t _buffer[BLOCK_CAPACITY];
Block* _head;
};
}

View File

@ -0,0 +1,20 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#pragma once
#include "../Arduino/Print.hpp"
namespace ArduinoJson {
namespace Internals {
// A dummy Print implementation used in JsonPrintable::measureLength()
class DummyPrint : public Print {
public:
virtual size_t write(uint8_t) { return 1; }
};
}
}

View File

@ -6,6 +6,7 @@
#pragma once
#include "DummyPrint.hpp"
#include "IndentedPrint.hpp"
#include "JsonWriter.hpp"
#include "Prettyfier.hpp"
@ -47,6 +48,16 @@ class JsonPrintable {
return prettyPrintTo(indentedPrint);
}
size_t measureLength() const {
DummyPrint dp;
return printTo(dp);
}
size_t measurePrettyLength() const {
DummyPrint dp;
return prettyPrintTo(dp);
}
private:
const T &downcast() const { return *static_cast<const T *>(this); }
};

View File

@ -71,6 +71,9 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
// It's a shortcut for JsonBuffer::createObject() and JsonArray::add()
JsonObject &createNestedObject();
// Removes element at specified index.
void removeAt(int index);
// Returns a reference an invalid JsonArray.
// This object is meant to replace a NULL pointer.
// This is used when memory allocation or JSON parsing fail.
@ -84,6 +87,8 @@ class JsonArray : public Internals::JsonPrintable<JsonArray>,
explicit JsonArray(JsonBuffer *buffer)
: Internals::List<JsonVariant>(buffer) {}
node_type *getNodeAt(int index) const;
// The instance returned by JsonArray::invalid()
static JsonArray _invalid;
};

View File

@ -20,10 +20,10 @@ build-env()
if [[ $(uname) == MINGW* ]]
then
build-env "Make" "MinGW Makefiles"
build-env "SublimeText" "Sublime Text 2 - MinGW Makefiles"
build-env "SublimeText" "Sublime Text 2 - Ninja"
build-env "VisualStudio" "Visual Studio 12 2013"
else
build-env "SublimeText" "Sublime Text 2 - Unix Makefiles"
build-env "SublimeText" "Sublime Text 2 - Ninja"
build-env "Make" "Unix Makefiles"
build-env "Xcode" "Xcode"
fi

View File

@ -8,7 +8,8 @@
#include "../../include/ArduinoJson/Arduino/Print.hpp"
#include <stdio.h> // for sprintf
#include <math.h> // for isnan() and isinf()
#include <stdio.h> // for sprintf()
size_t Print::print(const char s[]) {
size_t n = 0;
@ -19,8 +20,24 @@ size_t Print::print(const char s[]) {
}
size_t Print::print(double value, int digits) {
// https://github.com/arduino/Arduino/blob/db8cbf24c99dc930b9ccff1a43d018c81f178535/hardware/arduino/sam/cores/arduino/Print.cpp#L218
if (isnan(value)) return print("nan");
if (isinf(value)) return print("inf");
char tmp[32];
sprintf(tmp, "%.*f", digits, value);
// https://github.com/arduino/Arduino/blob/db8cbf24c99dc930b9ccff1a43d018c81f178535/hardware/arduino/sam/cores/arduino/Print.cpp#L220
bool isBigDouble = value > 4294967040.0 || value < -4294967040.0;
if (isBigDouble) {
// Arduino's implementation prints "ovf"
// We prefer trying to use scientific notation, since we have sprintf
sprintf(tmp, "%g", value);
} else {
// Here we have the exact same output as Arduino's implementation
sprintf(tmp, "%.*f", digits, value);
}
return print(tmp);
}

79
src/DynamicJsonBuffer.cpp Normal file
View File

@ -0,0 +1,79 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include "../include/ArduinoJson/DynamicJsonBuffer.hpp"
namespace ArduinoJson {
namespace Internals {
struct DynamicJsonBufferBlockWithoutData {
DynamicJsonBufferBlock* next;
size_t capacity;
size_t size;
};
struct DynamicJsonBufferBlock : DynamicJsonBufferBlockWithoutData {
uint8_t data[1];
};
}
}
using namespace ArduinoJson;
using namespace ArduinoJson::Internals;
DynamicJsonBuffer::DynamicJsonBuffer() {
_head = createBlock(FIRST_BLOCK_CAPACITY);
}
DynamicJsonBuffer::~DynamicJsonBuffer() {
Block* currentBlock = _head;
while (currentBlock != NULL) {
Block* nextBlock = currentBlock->next;
free(currentBlock);
currentBlock = nextBlock;
}
}
size_t DynamicJsonBuffer::size() const {
size_t total = 0;
for (const Block* b = _head; b != NULL; b = b->next) {
total += b->size;
}
return total;
}
void* DynamicJsonBuffer::alloc(size_t bytes) {
if (!canAllocInHead(bytes)) addNewBlock();
return allocInHead(bytes);
}
bool DynamicJsonBuffer::canAllocInHead(size_t bytes) const {
return _head->size + bytes <= _head->capacity;
}
void* DynamicJsonBuffer::allocInHead(size_t bytes) {
void* p = _head->data + _head->size;
_head->size += bytes;
return p;
}
void DynamicJsonBuffer::addNewBlock() {
Block* block = createBlock(_head->capacity * 2);
block->next = _head;
_head = block;
}
DynamicJsonBuffer::Block* DynamicJsonBuffer::createBlock(size_t capacity) {
size_t blkSize = sizeof(DynamicJsonBufferBlockWithoutData) + capacity;
Block* block = static_cast<Block*>(malloc(blkSize));
block->capacity = capacity;
block->size = 0;
block->next = NULL;
return block;
}

View File

@ -58,46 +58,44 @@ static char unescapeChar(char c) {
static inline bool isQuote(char c) { return c == '\"' || c == '\''; }
char *QuotedString::extractFrom(char *input, char **endPtr) {
char firstChar = *input;
if (!isQuote(firstChar)) {
// must start with a quote
return NULL;
}
char stopChar = firstChar; // closing quote is the same as opening quote
char *startPtr = input + 1; // skip the quote
char *readPtr = startPtr;
char *writePtr = startPtr;
char c;
char firstChar = *input;
char stopChar = firstChar; // closing quote is the same as opening quote
if (!isQuote(firstChar)) goto ERROR_OPENING_QUOTE_MISSING;
for (;;) {
c = *readPtr++;
if (c == '\0') {
// premature ending
return NULL;
}
if (c == '\0') goto ERROR_CLOSING_QUOTE_MISSING;
if (c == stopChar) {
// closing quote
break;
}
if (c == stopChar) goto SUCCESS;
if (c == '\\') {
// replace char
c = unescapeChar(*readPtr++);
if (c == '\0') goto ERROR_ESCAPE_SEQUENCE_INTERRUPTED;
}
*writePtr++ = c;
}
SUCCESS:
// end the string here
*writePtr = '\0';
// update end ptr
*endPtr = readPtr;
// return pointer to unquoted string
return startPtr;
ERROR_OPENING_QUOTE_MISSING:
ERROR_CLOSING_QUOTE_MISSING:
ERROR_ESCAPE_SEQUENCE_INTERRUPTED:
return NULL;
}

View File

@ -15,8 +15,7 @@ using namespace ArduinoJson::Internals;
JsonArray JsonArray::_invalid(NULL);
JsonVariant &JsonArray::at(int index) const {
node_type *node = _firstNode;
while (node && index--) node = node->next;
node_type *node = getNodeAt(index);
return node ? node->content : JsonVariant::invalid();
}
@ -43,6 +42,14 @@ JsonObject &JsonArray::createNestedObject() {
return object;
}
JsonArray::node_type *JsonArray::getNodeAt(int index) const {
node_type *node = _firstNode;
while (node && index--) node = node->next;
return node;
}
void JsonArray::removeAt(int index) { removeNode(getNodeAt(index)); }
void JsonArray::writeTo(JsonWriter &writer) const {
writer.beginArray();

View File

@ -64,13 +64,13 @@ void JsonVariant::set(long value) {
void JsonVariant::set(JsonArray &array) {
if (_type == JSON_INVALID) return;
_type = JSON_ARRAY;
_type = array.success() ? JSON_ARRAY : JSON_INVALID;
_content.asArray = &array;
}
void JsonVariant::set(JsonObject &object) {
if (_type == JSON_INVALID) return;
_type = JSON_OBJECT;
_type = object.success() ? JSON_OBJECT : JSON_INVALID;
_content.asObject = &object;
}

View File

@ -20,40 +20,11 @@ TEST_F(DynamicJsonBuffer_Basic_Tests, InitialSizeIsZero) {
ASSERT_EQ(0, buffer.size());
}
TEST_F(DynamicJsonBuffer_Basic_Tests, InitialBlockCountIsOne) {
ASSERT_EQ(1, buffer.blockCount());
}
TEST_F(DynamicJsonBuffer_Basic_Tests, SizeIncreasesAfterAlloc) {
buffer.alloc(1);
ASSERT_EQ(1, buffer.size());
buffer.alloc(1);
ASSERT_EQ(2, buffer.size());
buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY);
ASSERT_EQ(2 + DynamicJsonBuffer::BLOCK_CAPACITY, buffer.size());
}
TEST_F(DynamicJsonBuffer_Basic_Tests, BlockCountDoesntChangeWhenNotFull) {
buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY);
ASSERT_EQ(1, buffer.blockCount());
}
TEST_F(DynamicJsonBuffer_Basic_Tests, BlockCountChangesWhenFull) {
buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY);
buffer.alloc(1);
ASSERT_EQ(2, buffer.blockCount());
}
TEST_F(DynamicJsonBuffer_Basic_Tests, CanAllocLessThanBlockCapacity) {
void* p1 = buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY);
ASSERT_FALSE(p1 == NULL);
void* p2 = buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY);
ASSERT_FALSE(p2 == NULL);
}
TEST_F(DynamicJsonBuffer_Basic_Tests, CantAllocMoreThanBlockCapacity) {
void* p = buffer.alloc(DynamicJsonBuffer::BLOCK_CAPACITY + 1);
ASSERT_TRUE(p == NULL);
}
TEST_F(DynamicJsonBuffer_Basic_Tests, ReturnDifferentPointer) {

47
test/Issue67.cpp Normal file
View File

@ -0,0 +1,47 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include <gtest/gtest.h>
#include <ArduinoJson.h>
class Issue67 : public testing::Test {
public:
void whenInputIs(double value) { _variant = value; }
void outputMustBe(const char* expected) {
char buffer[1024];
_variant.printTo(buffer, sizeof(buffer));
ASSERT_STREQ(expected, buffer);
}
private:
JsonVariant _variant;
};
TEST_F(Issue67, BigPositiveDouble) {
whenInputIs(1e100);
outputMustBe("1e+100");
}
TEST_F(Issue67, BigNegativeDouble) {
whenInputIs(-1e100);
outputMustBe("-1e+100");
}
TEST_F(Issue67, Zero) {
whenInputIs(0.0);
outputMustBe("0.00");
}
TEST_F(Issue67, SmallPositiveDouble) {
whenInputIs(111.111);
outputMustBe("111.11");
}
TEST_F(Issue67, SmallNegativeDouble) {
whenInputIs(-111.111);
outputMustBe("-111.11");
}

View File

@ -48,6 +48,11 @@ class JsonArray_Container_Tests : public ::testing::Test {
}
};
template <>
void JsonArray_Container_Tests::itemMustEqual(int index, const char* expected) {
EXPECT_STREQ(expected, _array[index].asString());
}
TEST_F(JsonArray_Container_Tests, SuccessIsTrue) {
EXPECT_TRUE(_array.success());
}
@ -87,14 +92,11 @@ TEST_F(JsonArray_Container_Tests, CanStoreBooleans) {
}
TEST_F(JsonArray_Container_Tests, CanStoreStrings) {
const char* firstString = "h3110";
const char* secondString = "w0r1d";
_array.add("hello");
_array.add("world");
_array.add(firstString);
_array.add(secondString);
firstMustEqual(firstString);
secondMustEqual(secondString);
firstMustEqual("hello");
secondMustEqual("world");
}
TEST_F(JsonArray_Container_Tests, CanStoreNestedArrays) {
@ -134,3 +136,39 @@ TEST_F(JsonArray_Container_Tests, CanCreateNestedObjects) {
firstMustReference(innerObject1);
secondMustReference(innerObject2);
}
TEST_F(JsonArray_Container_Tests, RemoveFirstElement) {
_array.add("one");
_array.add("two");
_array.add("three");
_array.removeAt(0);
sizeMustBe(2);
firstMustEqual("two");
secondMustEqual("three");
}
TEST_F(JsonArray_Container_Tests, RemoveMiddleElement) {
_array.add("one");
_array.add("two");
_array.add("three");
_array.removeAt(1);
sizeMustBe(2);
firstMustEqual("one");
secondMustEqual("three");
}
TEST_F(JsonArray_Container_Tests, RemoveLastElement) {
_array.add("one");
_array.add("two");
_array.add("three");
_array.removeAt(2);
sizeMustBe(2);
firstMustEqual("one");
secondMustEqual("two");
}

View File

@ -16,13 +16,15 @@ class JsonArray_PrettyPrintTo_Tests : public testing::Test {
JsonArray& array;
void outputMustBe(const char* expected) {
size_t n = array.prettyPrintTo(_buffer, sizeof(_buffer));
EXPECT_STREQ(expected, _buffer);
EXPECT_EQ(strlen(expected), n);
}
char actual[256];
private:
char _buffer[256];
size_t actualLen = array.prettyPrintTo(actual, sizeof(actual));
size_t measuredLen = array.measurePrettyLength();
EXPECT_STREQ(expected, actual);
EXPECT_EQ(strlen(expected), actualLen);
EXPECT_EQ(strlen(expected), measuredLen);
}
};
TEST_F(JsonArray_PrettyPrintTo_Tests, Empty) { outputMustBe("[]"); }

View File

@ -16,9 +16,12 @@ class JsonArray_PrintTo_Tests : public testing::Test {
JsonArray &array;
void outputMustBe(const char *expected) {
size_t n = array.printTo(buffer, sizeof(buffer));
size_t actualLen = array.printTo(buffer, sizeof(buffer));
size_t measuredLen = array.measureLength();
EXPECT_STREQ(expected, buffer);
EXPECT_EQ(strlen(expected), n);
EXPECT_EQ(strlen(expected), actualLen);
EXPECT_EQ(strlen(expected), measuredLen);
}
private:

View File

@ -16,13 +16,15 @@ class JsonObject_PrettyPrintTo_Tests : public testing::Test {
JsonObject &_object;
void outputMustBe(const char *expected) {
size_t n = _object.prettyPrintTo(buffer, sizeof(buffer));
EXPECT_STREQ(expected, buffer);
EXPECT_EQ(strlen(expected), n);
}
char buffer[256];
private:
char buffer[256];
size_t actualLen = _object.prettyPrintTo(buffer, sizeof(buffer));
size_t measuredLen = _object.measurePrettyLength();
EXPECT_STREQ(expected, buffer);
EXPECT_EQ(strlen(expected), actualLen);
EXPECT_EQ(strlen(expected), measuredLen);
}
};
TEST_F(JsonObject_PrettyPrintTo_Tests, EmptyObject) { outputMustBe("{}"); }

View File

@ -16,10 +16,12 @@ class JsonObject_PrintTo_Tests : public testing::Test {
protected:
void outputMustBe(const char *expected) {
char actual[256];
int result = object.printTo(actual, sizeof(actual));
size_t actualLen = object.printTo(actual, sizeof(actual));
size_t measuredLen = object.measureLength();
EXPECT_STREQ(expected, actual);
EXPECT_EQ(strlen(expected), result);
EXPECT_EQ(strlen(expected), actualLen);
EXPECT_EQ(strlen(expected), measuredLen);
}
StaticJsonBuffer<JSON_OBJECT_SIZE(2)> json;

View File

@ -62,11 +62,13 @@ TEST_F(JsonVariant_Undefined_Tests, CanBeSetToBool) {
}
TEST_F(JsonVariant_Undefined_Tests, CanBeSetToArray) {
variant = JsonArray::invalid();
DynamicJsonBuffer jsonBuffer;
variant = jsonBuffer.createArray();
EXPECT_TRUE(variant.success());
}
TEST_F(JsonVariant_Undefined_Tests, CanBeSetToObject) {
variant = JsonObject::invalid();
DynamicJsonBuffer jsonBuffer;
variant = jsonBuffer.createObject();
EXPECT_TRUE(variant.success());
}

View File

@ -16,6 +16,11 @@ class QuotedString_ExtractFrom_Tests : public testing::Test {
_result = QuotedString::extractFrom(_jsonString, &_trailing);
}
void whenInputIs(const char *json, size_t len) {
memcpy(_jsonString, json, len);
_result = QuotedString::extractFrom(_jsonString, &_trailing);
}
void resultMustBe(const char *expected) { EXPECT_STREQ(expected, _result); }
void trailingMustBe(const char *expected) {
@ -134,3 +139,8 @@ TEST_F(QuotedString_ExtractFrom_Tests, AllEscapedCharsTogether) {
whenInputIs("\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"");
resultMustBe("1\"2\\3/4\b5\f6\n7\r8\t9");
}
TEST_F(QuotedString_ExtractFrom_Tests, UnterminatedEscapeSequence) {
whenInputIs("\"\\\0\"", 4);
resultMustBe(0);
}

View File

@ -7,7 +7,7 @@
#include <gtest/gtest.h>
#include <ArduinoJson.h>
TEST(StaticJsonBuffer_Array_Tests, GrowsWithArray) {
TEST(StaticJsonBuffer_CreateArray_Tests, GrowsWithArray) {
StaticJsonBuffer<JSON_ARRAY_SIZE(2)> json;
JsonArray &array = json.createArray();
@ -20,21 +20,21 @@ TEST(StaticJsonBuffer_Array_Tests, GrowsWithArray) {
ASSERT_EQ(JSON_ARRAY_SIZE(2), json.size());
}
TEST(StaticJsonBuffer_Array_Tests, SucceedWhenBigEnough) {
TEST(StaticJsonBuffer_CreateArray_Tests, SucceedWhenBigEnough) {
StaticJsonBuffer<JSON_ARRAY_SIZE(0)> json;
JsonArray &array = json.createArray();
ASSERT_TRUE(array.success());
}
TEST(StaticJsonBuffer_Array_Tests, FailsWhenTooSmall) {
TEST(StaticJsonBuffer_CreateArray_Tests, FailsWhenTooSmall) {
StaticJsonBuffer<JSON_ARRAY_SIZE(0) - 1> json;
JsonArray &array = json.createArray();
ASSERT_FALSE(array.success());
}
TEST(StaticJsonBuffer_Array_Tests, ArrayDoesntGrowWhenFull) {
TEST(StaticJsonBuffer_CreateArray_Tests, ArrayDoesntGrowWhenFull) {
StaticJsonBuffer<JSON_ARRAY_SIZE(1)> json;
JsonArray &array = json.createArray();

View File

@ -7,7 +7,7 @@
#include <gtest/gtest.h>
#include <ArduinoJson.h>
TEST(StaticJsonBuffer_Object_Tests, GrowsWithObject) {
TEST(StaticJsonBuffer_CreateObject_Tests, GrowsWithObject) {
StaticJsonBuffer<JSON_OBJECT_SIZE(3)> json;
JsonObject &obj = json.createObject();
@ -23,21 +23,21 @@ TEST(StaticJsonBuffer_Object_Tests, GrowsWithObject) {
ASSERT_EQ(JSON_OBJECT_SIZE(2), json.size());
}
TEST(StaticJsonBuffer_Object_Tests, SucceedWhenBigEnough) {
TEST(StaticJsonBuffer_CreateObject_Tests, SucceedWhenBigEnough) {
StaticJsonBuffer<JSON_OBJECT_SIZE(0)> json;
JsonObject &object = json.createObject();
ASSERT_TRUE(object.success());
}
TEST(StaticJsonBuffer_Object_Tests, FailsWhenTooSmall) {
TEST(StaticJsonBuffer_CreateObject_Tests, FailsWhenTooSmall) {
StaticJsonBuffer<JSON_OBJECT_SIZE(0) - 1> json;
JsonObject &object = json.createObject();
ASSERT_FALSE(object.success());
}
TEST(StaticJsonBuffer_Object_Tests, ObjectDoesntGrowWhenFull) {
TEST(StaticJsonBuffer_CreateObject_Tests, ObjectDoesntGrowWhenFull) {
StaticJsonBuffer<JSON_OBJECT_SIZE(1)> json;
JsonObject &obj = json.createObject();

View File

@ -0,0 +1,72 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include <gtest/gtest.h>
#include <ArduinoJson.h>
class StaticJsonBuffer_ParseArray_Tests : public testing::Test {
protected:
void with(JsonBuffer& jsonBuffer) { _jsonBuffer = &jsonBuffer; }
void whenInputIs(const char* json) { strcpy(_jsonString, json); }
void parseMustSucceed() {
EXPECT_TRUE(_jsonBuffer->parseArray(_jsonString).success());
}
void parseMustFail() {
EXPECT_FALSE(_jsonBuffer->parseArray(_jsonString).success());
}
private:
JsonBuffer* _jsonBuffer;
char _jsonString[256];
};
TEST_F(StaticJsonBuffer_ParseArray_Tests, TooSmallBufferForEmptyArray) {
StaticJsonBuffer<JSON_ARRAY_SIZE(0) - 1> bufferTooSmall;
with(bufferTooSmall);
whenInputIs("[]");
parseMustFail();
}
TEST_F(StaticJsonBuffer_ParseArray_Tests, BufferOfTheRightSizeForEmptyArray) {
StaticJsonBuffer<JSON_ARRAY_SIZE(0)> bufferOfRightSize;
with(bufferOfRightSize);
whenInputIs("[]");
parseMustSucceed();
}
TEST_F(StaticJsonBuffer_ParseArray_Tests, TooSmallBufferForArrayWithOneValue) {
StaticJsonBuffer<JSON_ARRAY_SIZE(1) - 1> bufferTooSmall;
with(bufferTooSmall);
whenInputIs("[1]");
parseMustFail();
}
TEST_F(StaticJsonBuffer_ParseArray_Tests,
BufferOfTheRightSizeForArrayWithOneValue) {
StaticJsonBuffer<JSON_ARRAY_SIZE(1)> bufferOfRightSize;
with(bufferOfRightSize);
whenInputIs("[1]");
parseMustSucceed();
}
TEST_F(StaticJsonBuffer_ParseArray_Tests,
TooSmallBufferForArrayWithNestedObject) {
StaticJsonBuffer<JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(0) - 1> bufferTooSmall;
with(bufferTooSmall);
whenInputIs("[{}]");
parseMustFail();
}
TEST_F(StaticJsonBuffer_ParseArray_Tests,
BufferOfTheRightSizeForArrayWithNestedObject) {
StaticJsonBuffer<JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(0)> bufferOfRightSize;
with(bufferOfRightSize);
whenInputIs("[{}]");
parseMustSucceed();
}

View File

@ -0,0 +1,73 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include <gtest/gtest.h>
#include <ArduinoJson.h>
class StaticJsonBuffer_ParseObject_Tests : public testing::Test {
protected:
void with(JsonBuffer& jsonBuffer) { _jsonBuffer = &jsonBuffer; }
void whenInputIs(const char* json) { strcpy(_jsonString, json); }
void parseMustSucceed() {
EXPECT_TRUE(_jsonBuffer->parseObject(_jsonString).success());
}
void parseMustFail() {
EXPECT_FALSE(_jsonBuffer->parseObject(_jsonString).success());
}
private:
JsonBuffer* _jsonBuffer;
char _jsonString[256];
};
TEST_F(StaticJsonBuffer_ParseObject_Tests, TooSmallBufferForEmptyObject) {
StaticJsonBuffer<JSON_OBJECT_SIZE(0) - 1> bufferTooSmall;
with(bufferTooSmall);
whenInputIs("{}");
parseMustFail();
}
TEST_F(StaticJsonBuffer_ParseObject_Tests, BufferOfTheRightSizeForEmptyObject) {
StaticJsonBuffer<JSON_OBJECT_SIZE(0)> bufferOfRightSize;
with(bufferOfRightSize);
whenInputIs("{}");
parseMustSucceed();
}
TEST_F(StaticJsonBuffer_ParseObject_Tests,
TooSmallBufferForObjectWithOneValue) {
StaticJsonBuffer<JSON_OBJECT_SIZE(1) - 1> bufferTooSmall;
with(bufferTooSmall);
whenInputIs("{\"a\":1}");
parseMustFail();
}
TEST_F(StaticJsonBuffer_ParseObject_Tests,
BufferOfTheRightSizeForObjectWithOneValue) {
StaticJsonBuffer<JSON_OBJECT_SIZE(1)> bufferOfRightSize;
with(bufferOfRightSize);
whenInputIs("{\"a\":1}");
parseMustSucceed();
}
TEST_F(StaticJsonBuffer_ParseObject_Tests,
TooSmallBufferForObjectWithNestedObject) {
StaticJsonBuffer<JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(0) - 1> bufferTooSmall;
with(bufferTooSmall);
whenInputIs("{\"a\":[]}");
parseMustFail();
}
TEST_F(StaticJsonBuffer_ParseObject_Tests,
BufferOfTheRightSizeForObjectWithNestedObject) {
StaticJsonBuffer<JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(0)> bufferOfRightSize;
with(bufferOfRightSize);
whenInputIs("{\"a\":[]}");
parseMustSucceed();
}