Compare commits

...

2 Commits

Author SHA1 Message Date
92e687303d Added support of non standard JSON input (issue #44) 2015-07-10 22:11:26 +02:00
78ae0b8aee Updated changelog 2015-05-31 14:42:09 +02:00
30 changed files with 409 additions and 401 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
/build
/bin
/lib
/sftp-config.json

View File

@ -4,9 +4,7 @@ compiler:
- clang
before_install:
- sudo pip install cpp-coveralls
before_script:
- cmake -DCOVERAGE=true .
script:
- make && make test
- cmake -DCOVERAGE=true . && make && make test
after_success:
- coveralls --exclude test --exclude third-party --gcov-options '\-lp'
- if [ "$CC" = "gcc" ]; then coveralls --exclude third-party --gcov-options '\-lp'; fi

View File

@ -1,12 +1,13 @@
ArduinoJson: change log
=======================
v5.0 (currently under development)
v5.0 (currently in beta)
----
* Added support of `String` class (issue #55, #56, #70, #77)
* Added support of non standard JSON input (issue #44)
* Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators
* Switched to new library layout (requires Arduino 1.0.6 or above)
* Switched to new the library layout (requires Arduino 1.0.6 or above)
**BREAKING CHANGES**:
- `JsonObject::add()` was renamed to `set()`
@ -17,9 +18,18 @@ v5.0 (currently under development)
Support of the `String` class has been added to the library because many people use it in their programs.
However, you should not see this as an invitation to use the `String` class.
The `String` class is **bad** because it uses dynamic memory allocation.
Compared to static allocation, it compiles to a bigger, slower program, and is less predicate.
Compared to static allocation, it compiles to a bigger, slower program, and is less predictable.
You certainly don't want that in an embedded environment!
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.
Special thanks to [Giancarlo Canales Barreto](https://github.com/gcanalesb) for finding this nasty bug.
v4.4
----

View File

@ -12,9 +12,8 @@ if(MSVC)
endif()
if(${COVERAGE})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -coverage")
set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
endif()
add_subdirectory(src)
add_subdirectory(test)
add_subdirectory(test)

View File

@ -0,0 +1,39 @@
// 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 {
class Encoding {
public:
// Optimized for code size on a 8-bit AVR
static char escapeChar(char c) {
const char *p = _escapeTable;
while (p[0] && p[1] != c) {
p += 2;
}
return p[0];
}
// Optimized for code size on a 8-bit AVR
static char unescapeChar(char c) {
const char *p = _escapeTable + 4;
for (;;) {
if (p[0] == '\0') return c;
if (p[0] == c) return p[1];
p += 2;
}
}
private:
static const char _escapeTable[];
};
}
}

View File

@ -18,7 +18,10 @@ namespace Internals {
class JsonParser {
public:
JsonParser(JsonBuffer *buffer, char *json, uint8_t nestingLimit)
: _buffer(buffer), _ptr(json), _nestingLimit(nestingLimit) {}
: _buffer(buffer),
_readPtr(json),
_writePtr(json),
_nestingLimit(nestingLimit) {}
JsonArray &parseArray();
JsonObject &parseObject();
@ -26,12 +29,10 @@ class JsonParser {
private:
bool skip(char charToSkip);
bool skip(const char *wordToSkip);
void skipSpaces();
bool parseAnythingTo(JsonVariant *destination);
FORCE_INLINE bool parseAnythingToUnsafe(JsonVariant *destination);
const char *parseString();
bool parseAnythingTo(JsonVariant *destination);
FORCE_INLINE bool parseAnythingToUnsafe(JsonVariant *destination);
inline bool parseArrayTo(JsonVariant *destination);
inline bool parseBooleanTo(JsonVariant *destination);
@ -41,7 +42,8 @@ class JsonParser {
inline bool parseStringTo(JsonVariant *destination);
JsonBuffer *_buffer;
char *_ptr;
const char *_readPtr;
char *_writePtr;
uint8_t _nestingLimit;
};
}

View File

@ -12,6 +12,10 @@
#include "Prettyfier.hpp"
#include "StringBuilder.hpp"
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
#include "StreamPrintAdapter.hpp"
#endif
namespace ArduinoJson {
namespace Internals {
@ -28,6 +32,14 @@ class JsonPrintable {
return writer.bytesWritten();
}
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
std::ostream& printTo(std::ostream &os) const {
StreamPrintAdapter adapter(os);
printTo(adapter);
return os;
}
#endif
size_t printTo(char *buffer, size_t bufferSize) const {
StringBuilder sb(buffer, bufferSize);
return printTo(sb);
@ -61,5 +73,13 @@ class JsonPrintable {
private:
const T &downcast() const { return *static_cast<const T *>(this); }
};
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
template<typename T>
inline std::ostream& operator<<(std::ostream& os, const JsonPrintable<T>& v) {
return v.printTo(os);
}
#endif
}
}

View File

@ -7,7 +7,7 @@
#pragma once
#include "../Arduino/Print.hpp"
#include "QuotedString.hpp"
#include "Encoding.hpp"
namespace ArduinoJson {
namespace Internals {
@ -26,7 +26,7 @@ class JsonWriter {
// Returns the number of bytes sent to the Print implementation.
// This is very handy for implementations of printTo() that must return the
// number of bytes written.
size_t bytesWritten() { return _length; }
size_t bytesWritten() const { return _length; }
void beginArray() { write('['); }
void endArray() { write(']'); }
@ -37,15 +37,32 @@ class JsonWriter {
void writeColon() { write(':'); }
void writeComma() { write(','); }
void writeBoolean(bool value) {
write(value ? "true" : "false");
}
void writeString(const char *value) {
_length += QuotedString::printTo(value, _sink);
if (!value) {
write("null");
} else {
write('\"');
while (*value) writeChar(*value++);
write('\"');
}
}
void writeChar(char c) {
char specialChar = Encoding::escapeChar(c);
if (specialChar) {
write('\\');
write(specialChar);
} else {
write(c);
}
}
void writeLong(long value) { _length += _sink.print(value); }
void writeBoolean(bool value) {
_length += _sink.print(value ? "true" : "false");
}
void writeDouble(double value, uint8_t decimals) {
_length += _sink.print(value, decimals);
}

View File

@ -1,29 +0,0 @@
// 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 {
// An helper class to print and extract doubly-quoted strings
class QuotedString {
public:
// Writes a doubly-quote string to a Print implementation.
// It adds the double quotes (") at the beginning and the end of the string.
// It escapes the special characters as required by the JSON specifications.
static size_t printTo(const char *, Print &);
// Reads a doubly-quoted string from a buffer.
// It removes the double quotes (").
// It unescapes the special character as required by the JSON specification,
// with the exception of the Unicode characters (\u0000).
static char *extractFrom(char *input, char **end);
};
}
}

View File

@ -0,0 +1,34 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#pragma once
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
#include "../Arduino/Print.hpp"
namespace ArduinoJson {
namespace Internals {
class StreamPrintAdapter : public Print {
public:
explicit StreamPrintAdapter(std::ostream& os) : _os(os) {}
virtual size_t write(uint8_t c) {
_os << static_cast<char>(c);
return 1;
}
private:
// cannot be assigned
StreamPrintAdapter& operator=(const StreamPrintAdapter&);
std::ostream& _os;
};
}
}
#endif // ARDUINOJSON_ENABLE_STD_STREAM

View File

@ -40,8 +40,19 @@ class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
return _array.is<T>(_index);
}
void writeTo(Internals::JsonWriter &writer) const {
_array.get(_index).writeTo(writer);
}
private:
JsonArray& _array;
const size_t _index;
};
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
inline std::ostream& operator<<(std::ostream& os, const JsonArraySubscript& source) {
return source.printTo(os);
}
#endif
} // namespace ArduinoJson

View File

@ -41,8 +41,18 @@ class JsonObjectSubscript : public JsonVariantBase<JsonObjectSubscript> {
return _object.is<T>(_key);
}
void writeTo(Internals::JsonWriter &writer) const {
_object.get(_key).writeTo(writer);
}
private:
JsonObject& _object;
JsonObjectKey _key;
};
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
inline std::ostream& operator<<(std::ostream& os, const JsonObjectSubscript& source) {
return source.printTo(os);
}
#endif
}

View File

@ -28,8 +28,7 @@ class JsonObject;
// - a char, short, int or a long (signed or unsigned)
// - a string (const char*)
// - a reference to a JsonArray or JsonObject
class JsonVariant : public Internals::JsonPrintable<JsonVariant>,
public JsonVariantBase<JsonVariant> {
class JsonVariant : public JsonVariantBase<JsonVariant> {
public:
// Creates an uninitialized JsonVariant
FORCE_INLINE JsonVariant() : _type(Internals::JSON_UNDEFINED) {}

View File

@ -186,4 +186,11 @@ template <>
inline bool JsonVariant::is<unsigned short>() const {
return _type == Internals::JSON_LONG;
}
#ifdef ARDUINOJSON_ENABLE_STD_STREAM
inline std::ostream& operator<<(std::ostream& os, const JsonVariant& source) {
return source.printTo(os);
}
#endif
} // namespace ArduinoJson

View File

@ -16,7 +16,7 @@ class JsonArraySubscript;
class JsonObjectSubscript;
template <typename TImpl>
class JsonVariantBase {
class JsonVariantBase : public Internals::JsonPrintable<TImpl> {
public:
// Gets the variant as a boolean value.
// Returns false if the variant is not a boolean value.
@ -79,6 +79,9 @@ class JsonVariantBase {
FORCE_INLINE const JsonObjectSubscript operator[](const char *key) const;
FORCE_INLINE const JsonObjectSubscript operator[](const String &key) const;
// Serialize the variant to a JsonWriter
void writeTo(Internals::JsonWriter &writer) const;
private:
const TImpl *impl() const { return static_cast<const TImpl *>(this); }
};

View File

@ -0,0 +1,12 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include "../../include/ArduinoJson/Internals/Encoding.hpp"
// How to escape special chars:
// _escapeTable[2*i+1] => the special char
// _escapeTable[2*i] => the char to use instead
const char ArduinoJson::Internals::Encoding::_escapeTable[] = "\"\"\\\\b\bf\fn\nr\rt\t";

View File

@ -9,7 +9,7 @@
#include <stdlib.h> // for strtol, strtod
#include <ctype.h>
#include "../../include/ArduinoJson/Internals/QuotedString.hpp"
#include "../../include/ArduinoJson/Internals/Encoding.hpp"
#include "../../include/ArduinoJson/JsonArray.hpp"
#include "../../include/ArduinoJson/JsonBuffer.hpp"
#include "../../include/ArduinoJson/JsonObject.hpp"
@ -17,25 +17,28 @@
using namespace ArduinoJson;
using namespace ArduinoJson::Internals;
void JsonParser::skipSpaces() {
while (isspace(*_ptr)) _ptr++;
static const char *skipSpaces(const char *ptr) {
while (isspace(*ptr)) ptr++;
return ptr;
}
bool JsonParser::skip(char charToSkip) {
skipSpaces();
if (*_ptr != charToSkip) return false;
_ptr++;
skipSpaces();
register const char *ptr = skipSpaces(_readPtr);
if (*ptr != charToSkip) return false;
ptr++;
_readPtr = skipSpaces(ptr);
return true;
}
bool JsonParser::skip(const char *wordToSkip) {
const char *charToSkip = wordToSkip;
while (*charToSkip && *_ptr == *charToSkip) {
charToSkip++;
_ptr++;
register const char *ptr = _readPtr;
while (*wordToSkip && *ptr == *wordToSkip) {
wordToSkip++;
ptr++;
}
return *charToSkip == '\0';
if (*wordToSkip != '\0') return false;
_readPtr = ptr;
return true;
}
bool JsonParser::parseAnythingTo(JsonVariant *destination) {
@ -47,9 +50,9 @@ bool JsonParser::parseAnythingTo(JsonVariant *destination) {
}
inline bool JsonParser::parseAnythingToUnsafe(JsonVariant *destination) {
skipSpaces();
_readPtr = skipSpaces(_readPtr);
switch (*_ptr) {
switch (*_readPtr) {
case '[':
return parseArrayTo(destination);
@ -181,7 +184,7 @@ bool JsonParser::parseBooleanTo(JsonVariant *destination) {
bool JsonParser::parseNumberTo(JsonVariant *destination) {
char *endOfLong;
long longValue = strtol(_ptr, &endOfLong, 10);
long longValue = strtol(_readPtr, &endOfLong, 10);
char stopChar = *endOfLong;
// Could it be a floating point value?
@ -189,14 +192,14 @@ bool JsonParser::parseNumberTo(JsonVariant *destination) {
if (couldBeFloat) {
// Yes => parse it as a double
double doubleValue = strtod(_ptr, &_ptr);
double doubleValue = strtod(_readPtr, const_cast<char **>(&_readPtr));
// Count the decimal digits
uint8_t decimals = static_cast<uint8_t>(_ptr - endOfLong - 1);
uint8_t decimals = static_cast<uint8_t>(_readPtr - endOfLong - 1);
// Set the variant as a double
*destination = JsonVariant(doubleValue, decimals);
} else {
// No => set the variant as a long
_ptr = endOfLong;
_readPtr = endOfLong;
*destination = longValue;
}
return true;
@ -209,8 +212,53 @@ bool JsonParser::parseNullTo(JsonVariant *destination) {
return true;
}
static bool isStopChar(char c) {
return c == '\0' || c == ':' || c == '}' || c == ']' || c == ',';
}
const char *JsonParser::parseString() {
return QuotedString::extractFrom(_ptr, &_ptr);
const char *readPtr = _readPtr;
char *writePtr = _writePtr;
char c = *readPtr;
if (c == '\'' || c == '\"') {
char stopChar = c;
for (;;) {
c = *++readPtr;
if (c == '\0') break;
if (c == stopChar) {
readPtr++;
break;
}
if (c == '\\') {
// replace char
c = Encoding::unescapeChar(*++readPtr);
if (c == '\0') break;
}
*writePtr++ = c;
}
} else {
for (;;) {
if (isStopChar(c)) break;
*writePtr++ = c;
c = *++readPtr;
}
}
// end the string here
*writePtr++ = '\0';
const char *startPtr = _writePtr;
// update end ptr
_readPtr = readPtr;
_writePtr = writePtr;
// return pointer to unquoted string
return startPtr;
}
bool JsonParser::parseStringTo(JsonVariant *destination) {

View File

@ -1,103 +0,0 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include "../../include/ArduinoJson/Internals/QuotedString.hpp"
using namespace ArduinoJson::Internals;
// How to escape special chars:
// specialChars[2*i+1] => the special char
// specialChars[2*i] => the char to use instead
static const char specialChars[] = "\"\"\\\\b\bf\fn\nr\rt\t";
static inline char getSpecialChar(char c) {
// Optimized for code size on a 8-bit AVR
const char *p = specialChars;
while (p[0] && p[1] != c) {
p += 2;
}
return p[0];
}
static inline size_t printCharTo(char c, Print &p) {
char specialChar = getSpecialChar(c);
return specialChar ? p.write('\\') + p.write(specialChar) : p.write(c);
}
size_t QuotedString::printTo(const char *s, Print &p) {
if (!s) return p.print("null");
size_t n = p.write('\"');
while (*s) {
n += printCharTo(*s++, p);
}
return n + p.write('\"');
}
static char unescapeChar(char c) {
// Optimized for code size on a 8-bit AVR
const char *p = specialChars + 4;
for (;;) {
if (p[0] == '\0') return c;
if (p[0] == c) return p[1];
p += 2;
}
}
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;
for (;;) {
c = *readPtr++;
if (c == '\0') {
// premature ending
return NULL;
}
if (c == stopChar) {
// closing quote
break;
}
if (c == '\\') {
// replace char
c = unescapeChar(*readPtr++);
}
*writePtr++ = c;
}
// end the string here
*writePtr = '\0';
// update end ptr
*endPtr = readPtr;
return startPtr;
}

View File

@ -18,4 +18,4 @@ add_executable(ArduinoJsonTests
target_link_libraries(ArduinoJsonTests ArduinoJson)
add_test(ArduinoJsonTests ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ArduinoJsonTests)
add_test(ArduinoJsonTests ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ArduinoJsonTests)

View File

@ -5,8 +5,8 @@
// https://github.com/bblanchon/ArduinoJson
#include <gtest/gtest.h>
#define ARDUINOJSON_ENABLE_STD_STREAM
#include <ArduinoJson.h>
#include "Printers.hpp"
class GbathreeBug : public testing::Test {
public:

View File

@ -5,8 +5,8 @@
// https://github.com/bblanchon/ArduinoJson
#include <gtest/gtest.h>
#define ARDUINOJSON_ENABLE_STD_STREAM
#include <ArduinoJson.h>
#include "Printers.hpp"
class JsonObject_Iterator_Test : public testing::Test {
public:

View File

@ -9,14 +9,19 @@
class JsonParser_Array_Tests : public testing::Test {
protected:
void whenInputIs(const char *json) {
strcpy(_jsonString, json);
_array = &_jsonBuffer.parseArray(_jsonString);
void whenInputIs(const char *json) { strcpy(_jsonString, json); }
void whenInputIs(const char *json, size_t len) {
memcpy(_jsonString, json, len);
}
void parseMustSucceed() { EXPECT_TRUE(_array->success()); }
void parseMustSucceed() {
_array = &_jsonBuffer.parseArray(_jsonString);
EXPECT_TRUE(_array->success());
}
void parseMustFail() {
_array = &_jsonBuffer.parseArray(_jsonString);
EXPECT_FALSE(_array->success());
EXPECT_EQ(0, _array->size());
}
@ -154,6 +159,11 @@ TEST_F(JsonParser_Array_Tests, IncompleteFalse) {
parseMustFail();
}
TEST_F(JsonParser_Array_Tests, MixedTrueFalse) {
whenInputIs("[trufalse]");
parseMustFail();
}
TEST_F(JsonParser_Array_Tests, TwoStrings) {
whenInputIs("[\"hello\",\"world\"]");
@ -162,3 +172,55 @@ TEST_F(JsonParser_Array_Tests, TwoStrings) {
firstElementMustBe("hello");
secondElementMustBe("world");
}
TEST_F(JsonParser_Array_Tests, EmptyStringsDoubleQuotes) {
whenInputIs("[\"\",\"\"]");
parseMustSucceed();
sizeMustBe(2);
firstElementMustBe("");
secondElementMustBe("");
}
TEST_F(JsonParser_Array_Tests, EmptyStringSingleQuotes) {
whenInputIs("[\'\',\'\']");
parseMustSucceed();
sizeMustBe(2);
firstElementMustBe("");
secondElementMustBe("");
}
TEST_F(JsonParser_Array_Tests, EmptyStringNoQuotes) {
whenInputIs("[,]");
parseMustSucceed();
sizeMustBe(2);
firstElementMustBe("");
secondElementMustBe("");
}
TEST_F(JsonParser_Array_Tests, ClosingDoubleQuoteMissing) {
whenInputIs("[\"]");
parseMustFail();
}
TEST_F(JsonParser_Array_Tests, ClosingSignleQuoteMissing) {
whenInputIs("[\']");
parseMustFail();
}
TEST_F(JsonParser_Array_Tests, StringWithEscapedChars) {
whenInputIs("[\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"]");
parseMustSucceed();
sizeMustBe(1);
firstElementMustBe("1\"2\\3/4\b5\f6\n7\r8\t9");
}
TEST_F(JsonParser_Array_Tests, StringWithUnterminatedEscapeSequence) {
whenInputIs("\"\\\0\"", 4);
parseMustFail();
}

View File

@ -75,6 +75,13 @@ TEST_F(JsonParser_Object_Test, OneStringSingleQuotes) {
keyMustHaveValue("key", "value");
}
TEST_F(JsonParser_Object_Test, OneStringNoQuotes) {
whenInputIs("{key:value}");
parseMustSucceed();
sizeMustBe(1);
keyMustHaveValue("key", "value");
}
TEST_F(JsonParser_Object_Test, OneStringSpaceBeforeKey) {
whenInputIs("{ \"key\":\"value\"}");
parseMustSucceed();

View File

@ -5,8 +5,8 @@
// https://github.com/bblanchon/ArduinoJson
#include <gtest/gtest.h>
#define ARDUINOJSON_ENABLE_STD_STREAM
#include <ArduinoJson/JsonVariant.hpp>
#include "Printers.hpp"
using namespace ArduinoJson;

View File

@ -5,8 +5,8 @@
// https://github.com/bblanchon/ArduinoJson
#include <gtest/gtest.h>
#define ARDUINOJSON_ENABLE_STD_STREAM
#include <ArduinoJson.h>
#include "Printers.hpp"
class JsonVariant_Undefined_Tests : public ::testing::Test {
protected:

View File

@ -6,16 +6,18 @@
#include <gtest/gtest.h>
#include <ArduinoJson/Internals/QuotedString.hpp>
#include <ArduinoJson/Internals/JsonWriter.hpp>
#include <ArduinoJson/Internals/StringBuilder.hpp>
using namespace ArduinoJson::Internals;
class QuotedString_PrintTo_Tests : public testing::Test {
class JsonWriter_WriteString_Tests : public testing::Test {
protected:
void whenInputIs(const char *input) {
StringBuilder sb(buffer, sizeof(buffer));
returnValue = QuotedString::printTo(input, sb);
JsonWriter writer(sb);
writer.writeString(input);
returnValue = writer.bytesWritten();
}
void outputMustBe(const char *expected) {
@ -28,52 +30,52 @@ class QuotedString_PrintTo_Tests : public testing::Test {
size_t returnValue;
};
TEST_F(QuotedString_PrintTo_Tests, Null) {
TEST_F(JsonWriter_WriteString_Tests, Null) {
whenInputIs(0);
outputMustBe("null");
}
TEST_F(QuotedString_PrintTo_Tests, EmptyString) {
TEST_F(JsonWriter_WriteString_Tests, EmptyString) {
whenInputIs("");
outputMustBe("\"\"");
}
TEST_F(QuotedString_PrintTo_Tests, QuotationMark) {
TEST_F(JsonWriter_WriteString_Tests, QuotationMark) {
whenInputIs("\"");
outputMustBe("\"\\\"\"");
}
TEST_F(QuotedString_PrintTo_Tests, ReverseSolidus) {
TEST_F(JsonWriter_WriteString_Tests, ReverseSolidus) {
whenInputIs("\\");
outputMustBe("\"\\\\\"");
}
TEST_F(QuotedString_PrintTo_Tests, Solidus) {
TEST_F(JsonWriter_WriteString_Tests, Solidus) {
whenInputIs("/");
outputMustBe("\"/\""); // but the JSON format allows \/
}
TEST_F(QuotedString_PrintTo_Tests, Backspace) {
TEST_F(JsonWriter_WriteString_Tests, Backspace) {
whenInputIs("\b");
outputMustBe("\"\\b\"");
}
TEST_F(QuotedString_PrintTo_Tests, Formfeed) {
TEST_F(JsonWriter_WriteString_Tests, Formfeed) {
whenInputIs("\f");
outputMustBe("\"\\f\"");
}
TEST_F(QuotedString_PrintTo_Tests, Newline) {
TEST_F(JsonWriter_WriteString_Tests, Newline) {
whenInputIs("\n");
outputMustBe("\"\\n\"");
}
TEST_F(QuotedString_PrintTo_Tests, CarriageReturn) {
TEST_F(JsonWriter_WriteString_Tests, CarriageReturn) {
whenInputIs("\r");
outputMustBe("\"\\r\"");
}
TEST_F(QuotedString_PrintTo_Tests, HorizontalTab) {
TEST_F(JsonWriter_WriteString_Tests, HorizontalTab) {
whenInputIs("\t");
outputMustBe("\"\\t\"");
}

View File

@ -1,50 +0,0 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include "Printers.hpp"
#include <ArduinoJson/JsonArray.hpp>
class StreamPrintAdapter : public Print {
public:
explicit StreamPrintAdapter(std::ostream& os) : _os(os) {}
virtual size_t write(uint8_t c) {
_os << static_cast<char>(c);
return 1;
}
private:
// cannot be assigned
StreamPrintAdapter& operator=(const StreamPrintAdapter&);
std::ostream& _os;
};
std::ostream& ArduinoJson::operator<<(std::ostream& os,
const ArduinoJson::JsonVariant& v) {
StreamPrintAdapter adapter(os);
v.printTo(adapter);
return os;
}
std::ostream& ArduinoJson::operator<<(std::ostream& os,
const ArduinoJson::JsonArray& v) {
StreamPrintAdapter adapter(os);
v.printTo(adapter);
return os;
}
std::ostream& ArduinoJson::operator<<(
std::ostream& os, const ArduinoJson::JsonObjectSubscript& v) {
JsonVariant value = v;
return os << value;
}
std::ostream& ArduinoJson::operator<<(
std::ostream& os, const ArduinoJson::JsonArraySubscript& v) {
JsonVariant value = v;
return os << value;
}

View File

@ -1,17 +0,0 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#pragma once
#include <ArduinoJson.h>
#include <ostream>
namespace ArduinoJson {
std::ostream& operator<<(std::ostream& os, const JsonVariant& v);
std::ostream& operator<<(std::ostream& os, const JsonArray& v);
std::ostream& operator<<(std::ostream& os, const JsonObjectSubscript& v);
std::ostream& operator<<(std::ostream& os, const JsonArraySubscript& v);
}

View File

@ -1,136 +0,0 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include <gtest/gtest.h>
#include <ArduinoJson/Internals/QuotedString.hpp>
using namespace ArduinoJson::Internals;
class QuotedString_ExtractFrom_Tests : public testing::Test {
protected:
void whenInputIs(const char *json) {
strcpy(_jsonString, json);
_result = QuotedString::extractFrom(_jsonString, &_trailing);
}
void resultMustBe(const char *expected) { EXPECT_STREQ(expected, _result); }
void trailingMustBe(const char *expected) {
EXPECT_STREQ(expected, _trailing);
}
private:
char _jsonString[256];
char *_result;
char *_trailing;
};
TEST_F(QuotedString_ExtractFrom_Tests, EmptyDoubleQuotedString) {
whenInputIs("\"\"");
resultMustBe("");
trailingMustBe("");
}
TEST_F(QuotedString_ExtractFrom_Tests, NoQuotes) {
whenInputIs("hello world");
resultMustBe(0);
}
TEST_F(QuotedString_ExtractFrom_Tests, MissingClosingQuote) {
whenInputIs("\"hello world");
resultMustBe(0);
}
TEST_F(QuotedString_ExtractFrom_Tests, EmptySingleQuotedString) {
whenInputIs("''");
resultMustBe("");
trailingMustBe("");
}
TEST_F(QuotedString_ExtractFrom_Tests, SimpleDoubleQuotedString) {
whenInputIs("\"hello world\"");
resultMustBe("hello world");
trailingMustBe("");
}
TEST_F(QuotedString_ExtractFrom_Tests, DoubleQuotedStringWithTrailing) {
whenInputIs("\"hello\" world");
resultMustBe("hello");
trailingMustBe(" world");
}
TEST_F(QuotedString_ExtractFrom_Tests, SingleQuotedStringWithTrailing) {
whenInputIs("'hello' world");
resultMustBe("hello");
trailingMustBe(" world");
}
TEST_F(QuotedString_ExtractFrom_Tests, CurlyBraces) {
whenInputIs("\"{hello:world}\"");
resultMustBe("{hello:world}");
}
TEST_F(QuotedString_ExtractFrom_Tests, SquareBraquets) {
whenInputIs("\"[hello,world]\"");
resultMustBe("[hello,world]");
}
TEST_F(QuotedString_ExtractFrom_Tests, EscapedDoubleQuote) {
whenInputIs("\"hello \\\"world\\\"\"");
resultMustBe("hello \"world\"");
}
TEST_F(QuotedString_ExtractFrom_Tests, EscapedSingleQuote) {
whenInputIs("\"hello \\\'world\\\'\"");
resultMustBe("hello 'world'");
}
TEST_F(QuotedString_ExtractFrom_Tests, EscapedSolidus) {
whenInputIs("\"hello \\/world\\/\"");
resultMustBe("hello /world/");
}
TEST_F(QuotedString_ExtractFrom_Tests, EscapedReverseSolidus) {
whenInputIs("\"hello \\\\world\\\\\"");
resultMustBe("hello \\world\\");
}
TEST_F(QuotedString_ExtractFrom_Tests, EscapedBackspace) {
whenInputIs("\"hello \\bworld\\b\"");
resultMustBe("hello \bworld\b");
}
TEST_F(QuotedString_ExtractFrom_Tests, EscapedFormfeed) {
whenInputIs("\"hello \\fworld\\f\"");
resultMustBe("hello \fworld\f");
}
TEST_F(QuotedString_ExtractFrom_Tests, EscapedNewline) {
whenInputIs("\"hello \\nworld\\n\"");
resultMustBe("hello \nworld\n");
}
TEST_F(QuotedString_ExtractFrom_Tests, EscapedCarriageReturn) {
whenInputIs("\"hello \\rworld\\r\"");
resultMustBe("hello \rworld\r");
}
TEST_F(QuotedString_ExtractFrom_Tests, EscapedTab) {
whenInputIs("\"hello \\tworld\\t\"");
resultMustBe("hello \tworld\t");
}
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");
}

62
test/StdStream.cpp Normal file
View File

@ -0,0 +1,62 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include <sstream>
#include <gtest/gtest.h>
#define ARDUINOJSON_ENABLE_STD_STREAM
#include <ArduinoJson.h>
TEST(StdStream, JsonVariantFalse) {
std::ostringstream os;
JsonVariant variant = false;
os << variant;
ASSERT_EQ("false", os.str());
}
TEST(StdStream, JsonVariantString) {
std::ostringstream os;
JsonVariant variant = "coucou";
os << variant;
ASSERT_EQ("\"coucou\"", os.str());
}
TEST(StdStream, JsonObject) {
std::ostringstream os;
DynamicJsonBuffer jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
object["key"] = "value";
os << object;
ASSERT_EQ("{\"key\":\"value\"}", os.str());
}
TEST(StdStream, JsonObjectSubscript) {
std::ostringstream os;
DynamicJsonBuffer jsonBuffer;
JsonObject& object = jsonBuffer.createObject();
object["key"] = "value";
os << object["key"];
ASSERT_EQ("\"value\"", os.str());
}
TEST(StdStream, JsonArray) {
std::ostringstream os;
DynamicJsonBuffer jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
array.add("value");
os << array;
ASSERT_EQ("[\"value\"]", os.str());
}
TEST(StdStream, JsonArraySubscript) {
std::ostringstream os;
DynamicJsonBuffer jsonBuffer;
JsonArray& array = jsonBuffer.createArray();
array.add("value");
os << array[0];
ASSERT_EQ("\"value\"", os.str());
}