diff --git a/.travis.yml b/.travis.yml index 9605e80c..1f393a12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ matrix: apt: sources: ['ubuntu-toolchain-r-test'] packages: ['g++-5'] - env: SCRIPT=cmake GCC=5 SANITIZE=undefined + env: SCRIPT=cmake GCC=5 # SANITIZE=undefined - compiler: gcc addons: apt: diff --git a/CHANGELOG.md b/CHANGELOG.md index e03c7e07..dce5d9a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ HEAD * Added `measureJson()` and `measureJsonPretty()` * Added `deserializeMsgPack()` (issue #358) * Added example `MsgPackParser.ino` (issue #358) +* Added support for non zero-terminated strings (issue #704) * Removed `JsonBuffer::parseArray()`, `parseObject()` and `parse()` * Removed `JsonBuffer::createArray()` and `createObject()` * Removed `printTo()` and `prettyPrintTo()` diff --git a/src/ArduinoJson.hpp b/src/ArduinoJson.hpp index 71bc18e1..a1a01508 100644 --- a/src/ArduinoJson.hpp +++ b/src/ArduinoJson.hpp @@ -5,12 +5,10 @@ #pragma once #include "ArduinoJson/DynamicJsonDocument.hpp" -#include "ArduinoJson/MsgPack/MsgPackDeserializer.hpp" #include "ArduinoJson/StaticJsonDocument.hpp" #include "ArduinoJson/deserializeJson.hpp" #include "ArduinoJson/deserializeMsgPack.hpp" -#include "ArduinoJson/Json/Deserialization/JsonDeserializer.hpp" #include "ArduinoJson/Json/Serialization/JsonSerializer.hpp" #include "ArduinoJson/JsonArrayImpl.hpp" #include "ArduinoJson/JsonObjectImpl.hpp" diff --git a/src/ArduinoJson/Json/Deserialization/Comments.hpp b/src/ArduinoJson/Json/Deserialization/Comments.hpp deleted file mode 100644 index c2c48ebc..00000000 --- a/src/ArduinoJson/Json/Deserialization/Comments.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -namespace ArduinoJson { -namespace Internals { -template -void skipSpacesAndComments(TInput& input) { - for (;;) { - switch (input.current()) { - // spaces - case ' ': - case '\t': - case '\r': - case '\n': - input.move(); - continue; - - // comments - case '/': - switch (input.next()) { - // C-style block comment - case '*': - input.move(); // skip '/' - // no need to skip '*' - for (;;) { - input.move(); - if (input.current() == '\0') return; - if (input.current() == '*' && input.next() == '/') { - input.move(); // skip '*' - input.move(); // skip '/' - break; - } - } - break; - - // C++-style line comment - case '/': - // not need to skip "//" - for (;;) { - input.move(); - if (input.current() == '\0') return; - if (input.current() == '\n') break; - } - break; - - // not a comment, just a '/' - default: - return; - } - break; - - default: - return; - } - } -} -} -} diff --git a/src/ArduinoJson/Json/Deserialization/JsonDeserializer.hpp b/src/ArduinoJson/Json/Deserialization/JsonDeserializer.hpp index c8db8ffd..73026e37 100644 --- a/src/ArduinoJson/Json/Deserialization/JsonDeserializer.hpp +++ b/src/ArduinoJson/Json/Deserialization/JsonDeserializer.hpp @@ -7,10 +7,9 @@ #include "../../JsonError.hpp" #include "../../JsonVariant.hpp" #include "../../Memory/JsonBuffer.hpp" -#include "../../Strings/StringWriter.hpp" +#include "../../Reading/Reader.hpp" #include "../../TypeTraits/IsConst.hpp" #include "../Encoding.hpp" -#include "./Comments.hpp" namespace ArduinoJson { namespace Internals { @@ -23,11 +22,13 @@ class JsonDeserializer { : _buffer(buffer), _reader(reader), _writer(writer), - _nestingLimit(nestingLimit) {} + _nestingLimit(nestingLimit), + _loaded(false) {} JsonError parse(JsonVariant &variant) { - skipSpacesAndComments(_reader); + JsonError err = skipSpacesAndComments(); + if (err) return err; - switch (_reader.current()) { + switch (current()) { case '[': return parseArray(variant); @@ -42,15 +43,25 @@ class JsonDeserializer { private: JsonDeserializer &operator=(const JsonDeserializer &); // non-copiable - static bool eat(TReader &reader, char charToSkip) { - skipSpacesAndComments(reader); - if (reader.current() != charToSkip) return false; - reader.move(); - return true; + char current() { + if (!_loaded) { + if (_reader.ended()) + _current = 0; + else + _current = _reader.read(); + _loaded = true; + } + return _current; + } + + void move() { + _loaded = false; } FORCE_INLINE bool eat(char charToSkip) { - return eat(_reader, charToSkip); + if (current() != charToSkip) return false; + move(); + return true; } JsonError parseArray(JsonVariant &variant) { @@ -62,6 +73,12 @@ class JsonDeserializer { // Check opening braket if (!eat('[')) return JsonError::InvalidInput; + + // Skip spaces + JsonError err = skipSpacesAndComments(); + if (err) return err; + + // Empty array? if (eat(']')) return JsonError::Ok; // Read each value @@ -69,12 +86,16 @@ class JsonDeserializer { // 1 - Parse value JsonVariant value; _nestingLimit--; - JsonError error = parse(value); + err = parse(value); _nestingLimit++; - if (error != JsonError::Ok) return error; + if (err) return err; if (!array->add(value)) return JsonError::NoMemory; - // 2 - More values? + // 2 - Skip spaces + err = skipSpacesAndComments(); + if (err) return err; + + // 3 - More values? if (eat(']')) return JsonError::Ok; if (!eat(',')) return JsonError::InvalidInput; } @@ -89,31 +110,52 @@ class JsonDeserializer { // Check opening brace if (!eat('{')) return JsonError::InvalidInput; + + // Skip spaces + JsonError err = skipSpacesAndComments(); + if (err) return err; + + // Empty object? if (eat('}')) return JsonError::Ok; // Read each key value pair for (;;) { - // 1 - Parse key + // Parse key const char *key; - JsonError error = parseString(&key); - if (error) return error; + err = parseString(&key); + if (err) return err; + + // Skip spaces + err = skipSpacesAndComments(); + if (err) return err; + + // Colon if (!eat(':')) return JsonError::InvalidInput; - // 2 - Parse value + // Parse value JsonVariant value; _nestingLimit--; - error = parse(value); + err = parse(value); _nestingLimit++; - if (error != JsonError::Ok) return error; + if (err) return err; if (!object->set(key, value)) return JsonError::NoMemory; - // 3 - More keys/values? + // Skip spaces + err = skipSpacesAndComments(); + if (err) return err; + + // More keys/values? if (eat('}')) return JsonError::Ok; if (!eat(',')) return JsonError::InvalidInput; + + // Skip spaces + err = skipSpacesAndComments(); + if (err) return err; } } + JsonError parseValue(JsonVariant &variant) { - bool hasQuotes = isQuote(_reader.current()); + bool hasQuotes = isQuote(current()); const char *value; JsonError error = parseString(&value); if (error) return error; @@ -128,33 +170,35 @@ class JsonDeserializer { JsonError parseString(const char **result) { typename RemoveReference::type::String str = _writer.startString(); - skipSpacesAndComments(_reader); - char c = _reader.current(); + char c = current(); + if (c == '\0') return JsonError::IncompleteInput; if (isQuote(c)) { // quotes - _reader.move(); + move(); char stopChar = c; for (;;) { - c = _reader.current(); - if (c == '\0') break; - _reader.move(); - + c = current(); + move(); if (c == stopChar) break; + if (c == '\0') return JsonError::IncompleteInput; + if (c == '\\') { + c = current(); + if (c == 0) return JsonError::IncompleteInput; // replace char - c = Encoding::unescapeChar(_reader.current()); + c = Encoding::unescapeChar(c); if (c == '\0') return JsonError::InvalidInput; - _reader.move(); + move(); } str.append(c); } } else if (canBeInNonQuotedString(c)) { // no quotes do { - _reader.move(); + move(); str.append(c); - c = _reader.current(); + c = current(); } while (canBeInNonQuotedString(c)); } else { return JsonError::InvalidInput; @@ -178,41 +222,80 @@ class JsonDeserializer { return c == '\'' || c == '\"'; } + JsonError skipSpacesAndComments() { + for (;;) { + switch (current()) { + // end of string + case '\0': + return JsonError::IncompleteInput; + + // spaces + case ' ': + case '\t': + case '\r': + case '\n': + move(); + continue; + + // comments + case '/': + move(); // skip '/' + switch (current()) { + // block comment + case '*': { + move(); // skip '*' + bool wasStar = false; + for (;;) { + char c = current(); + if (c == '\0') return JsonError::IncompleteInput; + if (c == '/' && wasStar) { + move(); + break; + } + wasStar = c == '*'; + move(); + } + break; + } + + // trailing comment + case '/': + // no need to skip "//" + for (;;) { + move(); + char c = current(); + if (c == '\0') return JsonError::IncompleteInput; + if (c == '\n') break; + } + break; + + // not a comment, just a '/' + default: + return JsonError::InvalidInput; + } + break; + + default: + return JsonError::Ok; + } + } + } + JsonBuffer *_buffer; TReader _reader; TWriter _writer; uint8_t _nestingLimit; + char _current; + bool _loaded; }; -template -struct JsonParserBuilder { - typedef typename StringTraits::Reader InputReader; - typedef JsonDeserializer TParser; - - static TParser makeParser(TJsonBuffer *buffer, TString &json, - uint8_t nestingLimit) { - return TParser(buffer, InputReader(json), *buffer, nestingLimit); - } -}; - -template -struct JsonParserBuilder::value>::type> { - typedef typename StringTraits::Reader TReader; - typedef StringWriter TWriter; - typedef JsonDeserializer TParser; - - static TParser makeParser(TJsonBuffer *buffer, TChar *json, - uint8_t nestingLimit) { - return TParser(buffer, TReader(json), TWriter(json), nestingLimit); - } -}; - -template -inline typename JsonParserBuilder::TParser makeParser( - TJsonBuffer *buffer, TString &json, uint8_t nestingLimit) { - return JsonParserBuilder::makeParser(buffer, json, - nestingLimit); +template +JsonDeserializer makeJsonDeserializer(TJsonBuffer *buffer, + TReader reader, + TWriter writer, + uint8_t nestingLimit) { + return JsonDeserializer(buffer, reader, writer, + nestingLimit); } } // namespace Internals } // namespace ArduinoJson diff --git a/src/ArduinoJson/JsonError.hpp b/src/ArduinoJson/JsonError.hpp index 44060415..cc988bed 100644 --- a/src/ArduinoJson/JsonError.hpp +++ b/src/ArduinoJson/JsonError.hpp @@ -4,11 +4,15 @@ #pragma once +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +#endif + namespace ArduinoJson { class JsonError { public: - enum Code { Ok, TooDeep, NoMemory, InvalidInput }; + enum Code { Ok, TooDeep, NoMemory, InvalidInput, IncompleteInput }; JsonError(Code code) : _code(code) {} @@ -42,6 +46,8 @@ class JsonError { return "NoMemory"; case InvalidInput: return "InvalidInput"; + case IncompleteInput: + return "IncompleteInput"; default: return "???"; } diff --git a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp index e1a923b7..d7654756 100644 --- a/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp @@ -6,8 +6,9 @@ #include "../JsonVariant.hpp" #include "../Memory/JsonBuffer.hpp" -#include "../Strings/StringWriter.hpp" +#include "../Reading/Reader.hpp" #include "../TypeTraits/IsConst.hpp" +#include "../Writing/Writer.hpp" #include "./MsgPackError.hpp" #include "./endianess.hpp" #include "./ieee754.hpp" @@ -29,27 +30,28 @@ class MsgPackDeserializer { _nestingLimit(nestingLimit) {} MsgPackError parse(JsonVariant &variant) { - uint8_t c = readOne(); + uint8_t code; + if (!readByte(code)) return MsgPackError::IncompleteInput; - if ((c & 0x80) == 0) { - variant = c; + if ((code & 0x80) == 0) { + variant = code; return MsgPackError::Ok; } - if ((c & 0xe0) == 0xe0) { - variant = static_cast(c); + if ((code & 0xe0) == 0xe0) { + variant = static_cast(code); return MsgPackError::Ok; } - if ((c & 0xe0) == 0xa0) { - return readString(variant, c & 0x1f); + if ((code & 0xe0) == 0xa0) { + return readString(variant, code & 0x1f); } - if ((c & 0xf0) == 0x90) return readArray(variant, c & 0x0F); + if ((code & 0xf0) == 0x90) return readArray(variant, code & 0x0F); - if ((c & 0xf0) == 0x80) return readObject(variant, c & 0x0F); + if ((code & 0xf0) == 0x80) return readObject(variant, code & 0x0F); - switch (c) { + switch (code) { case 0xc0: variant = static_cast(0); return MsgPackError::Ok; @@ -63,81 +65,65 @@ class MsgPackDeserializer { return MsgPackError::Ok; case 0xcc: - variant = readInteger(); - return MsgPackError::Ok; + return readInteger(variant); case 0xcd: - variant = readInteger(); - return MsgPackError::Ok; + return readInteger(variant); case 0xce: - variant = readInteger(); - return MsgPackError::Ok; + return readInteger(variant); case 0xcf: #if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64 - variant = readInteger(); + return readInteger(variant); #else readInteger(); - variant = readInteger(); + return readInteger(variant); #endif - return MsgPackError::Ok; case 0xd0: - variant = readInteger(); - return MsgPackError::Ok; + return readInteger(variant); case 0xd1: - variant = readInteger(); - return MsgPackError::Ok; + return readInteger(variant); case 0xd2: - variant = readInteger(); - return MsgPackError::Ok; + return readInteger(variant); case 0xd3: #if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64 - variant = readInteger(); + return readInteger(variant); #else - readInteger(); - variant = readInteger(); + if (!skip(4)) return MsgPackError::IncompleteInput; + return readInteger(variant); #endif - return MsgPackError::Ok; case 0xca: - variant = readFloat(); - return MsgPackError::Ok; + return readFloat(variant); case 0xcb: - variant = readDouble(); - return MsgPackError::Ok; + return readDouble(variant); - case 0xd9: { - uint8_t n = readInteger(); - return readString(variant, n); - } + case 0xd9: + return readString(variant); - case 0xda: { - uint16_t n = readInteger(); - return readString(variant, n); - } + case 0xda: + return readString(variant); - case 0xdb: { - uint32_t n = readInteger(); - return readString(variant, n); - } + case 0xdb: + return readString(variant); case 0xdc: - return readArray(variant, readInteger()); + return readArray(variant); case 0xdd: - return readArray(variant, readInteger()); + return readArray(variant); case 0xde: - return readObject(variant, readInteger()); + return readObject(variant); case 0xdf: - return readObject(variant, readInteger()); + return readObject(variant); default: return MsgPackError::NotSupported; @@ -148,65 +134,115 @@ class MsgPackDeserializer { // Prevent VS warning "assignment operator could not be generated" MsgPackDeserializer &operator=(const MsgPackDeserializer &); - uint8_t readOne() { - char c = _reader.current(); - _reader.move(); - return static_cast(c); + bool skip(uint8_t n) { + while (n--) { + if (_reader.ended()) return false; + _reader.read(); + } + return true; } - void read(uint8_t *p, size_t n) { - for (size_t i = 0; i < n; i++) p[i] = readOne(); + bool readByte(uint8_t &value) { + if (_reader.ended()) return false; + value = static_cast(_reader.read()); + return true; + } + + bool readBytes(uint8_t *p, size_t n) { + for (size_t i = 0; i < n; i++) { + if (!readByte(p[i])) return false; + } + return true; } template - void read(T &value) { - read(reinterpret_cast(&value), sizeof(value)); + bool readBytes(T &value) { + return readBytes(reinterpret_cast(&value), sizeof(value)); } template T readInteger() { T value; - read(value); + readBytes(value); fixEndianess(value); return value; } template - typename EnableIf::type readFloat() { + bool readInteger(T &value) { + if (!readBytes(value)) return false; + fixEndianess(value); + return true; + } + + template + MsgPackError readInteger(JsonVariant &variant) { T value; - read(value); - fixEndianess(value); - return value; + if (!readInteger(value)) return MsgPackError::IncompleteInput; + variant = value; + return MsgPackError::Ok; } template - typename EnableIf::type readDouble() { + typename EnableIf::type readFloat( + JsonVariant &variant) { T value; - read(value); + if (!readBytes(value)) return MsgPackError::IncompleteInput; fixEndianess(value); - return value; + variant = value; + return MsgPackError::Ok; } template - typename EnableIf::type readDouble() { + typename EnableIf::type readDouble( + JsonVariant &variant) { + T value; + if (!readBytes(value)) return MsgPackError::IncompleteInput; + fixEndianess(value); + variant = value; + return MsgPackError::Ok; + } + + template + typename EnableIf::type readDouble( + JsonVariant &variant) { uint8_t i[8]; // input is 8 bytes T value; // output is 4 bytes uint8_t *o = reinterpret_cast(&value); - read(i, 8); + if (!readBytes(i, 8)) return MsgPackError::IncompleteInput; doubleToFloat(i, o); fixEndianess(value); - return value; + variant = value; + return MsgPackError::Ok; + } + + template + MsgPackError readString(JsonVariant &variant) { + T size; + if (!readInteger(size)) return MsgPackError::IncompleteInput; + return readString(variant, size); } MsgPackError readString(JsonVariant &variant, size_t n) { typename RemoveReference::type::String str = _writer.startString(); - for (; n; --n) str.append(static_cast(readOne())); + for (; n; --n) { + uint8_t c; + if (!readBytes(c)) return MsgPackError::IncompleteInput; + str.append(static_cast(c)); + } const char *s = str.c_str(); if (s == NULL) return MsgPackError::NoMemory; variant = s; return MsgPackError::Ok; } + template + MsgPackError readArray(JsonVariant &variant) { + TSize size; + if (!readInteger(size)) return MsgPackError::IncompleteInput; + return readArray(variant, size); + } + MsgPackError readArray(JsonVariant &variant, size_t n) { JsonArray *array = new (_buffer) JsonArray(_buffer); if (!array) return MsgPackError::NoMemory; @@ -227,6 +263,13 @@ class MsgPackDeserializer { return MsgPackError::Ok; } + template + MsgPackError readObject(JsonVariant &variant) { + TSize size; + if (!readInteger(size)) return MsgPackError::IncompleteInput; + return readObject(variant, size); + } + MsgPackError readObject(JsonVariant &variant, size_t n) { JsonObject *object = new (_buffer) JsonObject(_buffer); if (!object) return MsgPackError::NoMemory; @@ -258,37 +301,11 @@ class MsgPackDeserializer { uint8_t _nestingLimit; }; -template -struct MsgPackDeserializerBuilder { - typedef typename StringTraits::Reader InputReader; - typedef MsgPackDeserializer TParser; - - static TParser makeMsgPackDeserializer(TJsonBuffer *buffer, TString &json, - uint8_t nestingLimit) { - return TParser(buffer, InputReader(json), *buffer, nestingLimit); - } -}; - -template -struct MsgPackDeserializerBuilder< - TJsonBuffer, TChar *, typename EnableIf::value>::type> { - typedef typename StringTraits::Reader TReader; - typedef StringWriter TWriter; - typedef MsgPackDeserializer TParser; - - static TParser makeMsgPackDeserializer(TJsonBuffer *buffer, TChar *json, - uint8_t nestingLimit) { - return TParser(buffer, TReader(json), TWriter(json), nestingLimit); - } -}; - -template -inline typename MsgPackDeserializerBuilder::TParser -makeMsgPackDeserializer(TJsonBuffer *buffer, TString &json, - uint8_t nestingLimit) { - return MsgPackDeserializerBuilder< - TJsonBuffer, TString>::makeMsgPackDeserializer(buffer, json, - nestingLimit); +template +MsgPackDeserializer makeMsgPackDeserializer( + TJsonBuffer *buffer, TReader reader, TWriter writer, uint8_t nestingLimit) { + return MsgPackDeserializer(buffer, reader, writer, + nestingLimit); } } // namespace Internals } // namespace ArduinoJson diff --git a/src/ArduinoJson/MsgPack/MsgPackError.hpp b/src/ArduinoJson/MsgPack/MsgPackError.hpp index 0134a723..ed3a8a12 100644 --- a/src/ArduinoJson/MsgPack/MsgPackError.hpp +++ b/src/ArduinoJson/MsgPack/MsgPackError.hpp @@ -4,11 +4,15 @@ #pragma once +#if ARDUINOJSON_ENABLE_STD_STREAM +#include +#endif + namespace ArduinoJson { class MsgPackError { public: - enum Code { Ok, NotSupported, NoMemory, TooDeep }; + enum Code { Ok, NotSupported, NoMemory, TooDeep, IncompleteInput }; MsgPackError() {} @@ -44,6 +48,8 @@ class MsgPackError { return "NoMemory"; case TooDeep: return "TooDeep"; + case IncompleteInput: + return "IncompleteInput"; default: return "???"; } diff --git a/src/ArduinoJson/Reading/ArduinoStreamReader.hpp b/src/ArduinoJson/Reading/ArduinoStreamReader.hpp new file mode 100644 index 00000000..53260657 --- /dev/null +++ b/src/ArduinoJson/Reading/ArduinoStreamReader.hpp @@ -0,0 +1,41 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_ARDUINO_STREAM + +#include + +namespace ArduinoJson { +namespace Internals { + +struct ArduinoStreamReader { + Stream& _stream; + char _current; + bool _ended; + + public: + explicit ArduinoStreamReader(Stream& stream) + : _stream(stream), _current(0), _ended(false) {} + + char read() { + // don't use _stream.read() as it ignores the timeout + char c = 0; + _ended = _stream.readBytes(&c, 1) == 0; + return c; + } + + bool ended() const { + return _ended; + } +}; + +inline ArduinoStreamReader makeReader(Stream& input) { + return ArduinoStreamReader(input); +} +} // namespace Internals +} // namespace ArduinoJson + +#endif diff --git a/src/ArduinoJson/Reading/CharPointerReader.hpp b/src/ArduinoJson/Reading/CharPointerReader.hpp new file mode 100644 index 00000000..8414f84a --- /dev/null +++ b/src/ArduinoJson/Reading/CharPointerReader.hpp @@ -0,0 +1,64 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +template +class UnsafeCharPointerReader { + const TChar* _ptr; + + public: + explicit UnsafeCharPointerReader(const TChar* ptr) + : _ptr(ptr ? ptr : reinterpret_cast("")) {} + + char read() { + return static_cast(*_ptr++); + } + + bool ended() const { + // we cannot know + return false; + } +}; + +template +class SafeCharPointerReader { + const TChar* _ptr; + const TChar* _end; + + public: + explicit SafeCharPointerReader(const TChar* ptr, size_t len) + : _ptr(ptr ? ptr : reinterpret_cast("")), + _end(_ptr + len) {} + + char read() { + return static_cast(*_ptr++); + } + + bool ended() const { + return _ptr == _end; + } +}; + +template +inline UnsafeCharPointerReader makeReader(TChar* input) { + return UnsafeCharPointerReader(input); +} + +template +inline SafeCharPointerReader makeReader(TChar* input, size_t n) { + return SafeCharPointerReader(input, n); +} + +#if ARDUINOJSON_ENABLE_ARDUINO_STRING +inline SafeCharPointerReader makeReader(const String& input) { + return SafeCharPointerReader(input.c_str(), input.length()); +} +#endif + +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/Reading/FlashStringReader.hpp b/src/ArduinoJson/Reading/FlashStringReader.hpp new file mode 100644 index 00000000..2eabf798 --- /dev/null +++ b/src/ArduinoJson/Reading/FlashStringReader.hpp @@ -0,0 +1,56 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_PROGMEM + +namespace ArduinoJson { +namespace Internals { +class UnsafeFlashStringReader { + const char* _ptr; + + public: + explicit UnsafeFlashStringReader(const __FlashStringHelper* ptr) + : _ptr(reinterpret_cast(ptr)) {} + + char read() { + return pgm_read_byte_near(_ptr++); + } + + bool ended() const { + // this reader cannot detect the end + return false; + } +}; + +class SafeFlashStringReader { + const char* _ptr; + const char* _end; + + public: + explicit SafeFlashStringReader(const __FlashStringHelper* ptr, size_t size) + : _ptr(reinterpret_cast(ptr)), _end(_ptr + size) {} + + char read() { + return pgm_read_byte_near(_ptr++); + } + + bool ended() const { + return _ptr == _end; + } +}; + +inline UnsafeFlashStringReader makeReader(const __FlashStringHelper* input) { + return UnsafeFlashStringReader(input); +} + +inline SafeFlashStringReader makeReader(const __FlashStringHelper* input, + size_t size) { + return SafeFlashStringReader(input, size); +} +} // namespace Internals +} // namespace ArduinoJson + +#endif diff --git a/src/ArduinoJson/Reading/IteratorReader.hpp b/src/ArduinoJson/Reading/IteratorReader.hpp new file mode 100644 index 00000000..7e01c214 --- /dev/null +++ b/src/ArduinoJson/Reading/IteratorReader.hpp @@ -0,0 +1,34 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +namespace ArduinoJson { +namespace Internals { + +template +class IteratorReader { + TIterator _ptr, _end; + + public: + explicit IteratorReader(TIterator begin, TIterator end) + : _ptr(begin), _end(end) {} + + bool ended() const { + return _ptr == _end; + } + + char read() { + return char(*_ptr++); + } +}; + +template +inline IteratorReader makeReader( + const TInput& input) { + return IteratorReader(input.begin(), + input.end()); +} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/Reading/Reader.hpp b/src/ArduinoJson/Reading/Reader.hpp new file mode 100644 index 00000000..ba7e0146 --- /dev/null +++ b/src/ArduinoJson/Reading/Reader.hpp @@ -0,0 +1,11 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "./ArduinoStreamReader.hpp" +#include "./CharPointerReader.hpp" +#include "./FlashStringReader.hpp" +#include "./IteratorReader.hpp" +#include "./StdStreamReader.hpp" diff --git a/src/ArduinoJson/Reading/StdStreamReader.hpp b/src/ArduinoJson/Reading/StdStreamReader.hpp new file mode 100644 index 00000000..8ad841a7 --- /dev/null +++ b/src/ArduinoJson/Reading/StdStreamReader.hpp @@ -0,0 +1,40 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#if ARDUINOJSON_ENABLE_STD_STREAM + +#include + +namespace ArduinoJson { +namespace Internals { + +class StdStreamReader { + std::istream& _stream; + char _current; + + public: + explicit StdStreamReader(std::istream& stream) + : _stream(stream), _current(0) {} + + bool ended() const { + return _stream.eof(); + } + + char read() { + return static_cast(_stream.get()); + } + + private: + StdStreamReader& operator=(const StdStreamReader&); // Visual Studio C4512 +}; + +inline StdStreamReader makeReader(std::istream& input) { + return StdStreamReader(input); +} +} // namespace Internals +} // namespace ArduinoJson + +#endif diff --git a/src/ArduinoJson/Strings/ArduinoStream.hpp b/src/ArduinoJson/Strings/ArduinoStream.hpp deleted file mode 100644 index 5db0852b..00000000 --- a/src/ArduinoJson/Strings/ArduinoStream.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#if ARDUINOJSON_ENABLE_ARDUINO_STREAM - -#include - -namespace ArduinoJson { -namespace Internals { - -struct ArduinoStreamTraits { - class Reader { - Stream& _stream; - char _current, _next; - - public: - Reader(Stream& stream) : _stream(stream), _current(0), _next(0) {} - - void move() { - _current = _next; - _next = 0; - } - - char current() { - if (!_current) _current = read(); - return _current; - } - - char next() { - // assumes that current() has been called - if (!_next) _next = read(); - return _next; - } - - private: - char read() { - // don't use _stream.read() as it ignores the timeout - char c = 0; - _stream.readBytes(&c, 1); - return c; - } - }; - - static const bool has_append = false; - static const bool has_equals = false; -}; - -template -struct StringTraits< - TStream, - // match any type that is derived from Stream: - typename EnableIf< - IsBaseOf::type>::value>::type> - : ArduinoStreamTraits {}; -} -} - -#endif diff --git a/src/ArduinoJson/Strings/CharPointer.hpp b/src/ArduinoJson/Strings/CharPointer.hpp index a9f30f78..2b804ab3 100644 --- a/src/ArduinoJson/Strings/CharPointer.hpp +++ b/src/ArduinoJson/Strings/CharPointer.hpp @@ -9,26 +9,6 @@ namespace Internals { template struct CharPointerTraits { - class Reader { - const TChar* _ptr; - - public: - Reader(const TChar* ptr) - : _ptr(ptr ? ptr : reinterpret_cast("")) {} - - void move() { - ++_ptr; - } - - char current() const { - return char(_ptr[0]); - } - - char next() const { - return char(_ptr[1]); - } - }; - static bool equals(const TChar* str, const char* expected) { return strcmp(reinterpret_cast(str), expected) == 0; } diff --git a/src/ArduinoJson/Strings/FlashString.hpp b/src/ArduinoJson/Strings/FlashString.hpp index 95f555d2..f5edbdc6 100644 --- a/src/ArduinoJson/Strings/FlashString.hpp +++ b/src/ArduinoJson/Strings/FlashString.hpp @@ -10,26 +10,6 @@ namespace ArduinoJson { namespace Internals { template <> struct StringTraits { - class Reader { - const char* _ptr; - - public: - Reader(const __FlashStringHelper* ptr) - : _ptr(reinterpret_cast(ptr)) {} - - void move() { - _ptr++; - } - - char current() const { - return pgm_read_byte_near(_ptr); - } - - char next() const { - return pgm_read_byte_near(_ptr + 1); - } - }; - static bool equals(const __FlashStringHelper* str, const char* expected) { return strcmp_P(expected, (const char*)str) == 0; } diff --git a/src/ArduinoJson/Strings/StdStream.hpp b/src/ArduinoJson/Strings/StdStream.hpp deleted file mode 100644 index 227c7440..00000000 --- a/src/ArduinoJson/Strings/StdStream.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#pragma once - -#if ARDUINOJSON_ENABLE_STD_STREAM - -#include - -namespace ArduinoJson { -namespace Internals { - -struct StdStreamTraits { - class Reader { - std::istream& _stream; - char _current, _next; - - public: - Reader(std::istream& stream) : _stream(stream), _current(0), _next(0) {} - - void move() { - _current = _next; - _next = 0; - } - - char current() { - if (!_current) _current = read(); - return _current; - } - - char next() { - // assumes that current() has been called - if (!_next) _next = read(); - return _next; - } - - private: - Reader& operator=(const Reader&); // Visual Studio C4512 - - char read() { - return _stream.eof() ? '\0' : static_cast(_stream.get()); - } - }; - - static const bool has_append = false; - static const bool has_equals = false; -}; - -template -struct StringTraits< - TStream, - // match any type that is derived from std::istream: - typename EnableIf::type>::value>::type> - : StdStreamTraits {}; -} -} - -#endif diff --git a/src/ArduinoJson/Strings/StdString.hpp b/src/ArduinoJson/Strings/StdString.hpp index 39124dac..aa521e58 100644 --- a/src/ArduinoJson/Strings/StdString.hpp +++ b/src/ArduinoJson/Strings/StdString.hpp @@ -35,10 +35,6 @@ struct StdStringTraits { return !str.c_str(); } - struct Reader : CharPointerTraits::Reader { - Reader(const TString& str) : CharPointerTraits::Reader(str.c_str()) {} - }; - static bool equals(const TString& str, const char* expected) { return 0 == strcmp(str.c_str(), expected); } diff --git a/src/ArduinoJson/Strings/StringTraits.hpp b/src/ArduinoJson/Strings/StringTraits.hpp index dd5694b2..5201c172 100644 --- a/src/ArduinoJson/Strings/StringTraits.hpp +++ b/src/ArduinoJson/Strings/StringTraits.hpp @@ -29,8 +29,6 @@ struct StringTraits : StringTraits {}; } } -#include "ArduinoStream.hpp" #include "CharPointer.hpp" #include "FlashString.hpp" -#include "StdStream.hpp" #include "StdString.hpp" diff --git a/src/ArduinoJson/Writing/JsonBufferWriter.hpp b/src/ArduinoJson/Writing/JsonBufferWriter.hpp new file mode 100644 index 00000000..5631ac2e --- /dev/null +++ b/src/ArduinoJson/Writing/JsonBufferWriter.hpp @@ -0,0 +1,27 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "./StringWriter.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +class JsonBufferWriter { + public: + JsonBufferWriter(TJsonBuffer& jb) : _jb(&jb) {} + + typedef typename TJsonBuffer::String String; + + String startString() { + return _jb->startString(); + } + + private: + TJsonBuffer* _jb; +}; +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/Strings/StringWriter.hpp b/src/ArduinoJson/Writing/StringWriter.hpp similarity index 100% rename from src/ArduinoJson/Strings/StringWriter.hpp rename to src/ArduinoJson/Writing/StringWriter.hpp diff --git a/src/ArduinoJson/Writing/Writer.hpp b/src/ArduinoJson/Writing/Writer.hpp new file mode 100644 index 00000000..7a529279 --- /dev/null +++ b/src/ArduinoJson/Writing/Writer.hpp @@ -0,0 +1,44 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#pragma once + +#include "./JsonBufferWriter.hpp" +#include "./StringWriter.hpp" + +namespace ArduinoJson { +namespace Internals { + +template +struct Writer { + typedef JsonBufferWriter type; + + static type create(TJsonBuffer& jb, TInput&) { + return type(jb); + } +}; + +template +struct Writer::value>::type> { + typedef StringWriter type; + + static type create(TJsonBuffer&, TChar* input) { + return type(input); + } +}; + +template +typename Writer::type makeWriter(TJsonBuffer& jb, + TInput& input) { + return Writer::create(jb, input); +} + +template +typename Writer::type makeWriter(TJsonBuffer& jb, + TChar* input) { + return Writer::create(jb, input); +} +} // namespace Internals +} // namespace ArduinoJson diff --git a/src/ArduinoJson/deserializeJson.hpp b/src/ArduinoJson/deserializeJson.hpp index c59c8517..bda81ac6 100644 --- a/src/ArduinoJson/deserializeJson.hpp +++ b/src/ArduinoJson/deserializeJson.hpp @@ -5,34 +5,53 @@ #pragma once #include "Json/Deserialization/JsonDeserializer.hpp" +#include "Reading/Reader.hpp" +#include "Writing/Writer.hpp" namespace ArduinoJson { -// JsonError deserializeJson(TDocument& doc, TString json); +// JsonError deserializeJson(TDocument& doc, TString input); // TDocument = DynamicJsonDocument, StaticJsonDocument // TString = const std::string&, const String& template typename Internals::EnableIf::value, JsonError>::type -deserializeJson(TDocument &doc, const TString &json) { - return Internals::makeParser(&doc.buffer(), json, doc.nestingLimit) +deserializeJson(TDocument &doc, const TString &input) { + using namespace Internals; + return makeJsonDeserializer(&doc.buffer(), makeReader(input), + makeWriter(doc.buffer(), input), doc.nestingLimit) .parse(doc.template to()); } // -// JsonError deserializeJson(TDocument& doc, TString json); +// JsonError deserializeJson(TDocument& doc, TChar* input); // TDocument = DynamicJsonDocument, StaticJsonDocument -// TString = const char*, const char[N], const FlashStringHelper* -template -JsonError deserializeJson(TDocument &doc, TString *json) { - return Internals::makeParser(&doc.buffer(), json, doc.nestingLimit) +// TChar* = char*, const char*, const FlashStringHelper* +template +JsonError deserializeJson(TDocument &doc, TChar *input) { + using namespace Internals; + return makeJsonDeserializer(&doc.buffer(), makeReader(input), + makeWriter(doc.buffer(), input), doc.nestingLimit) .parse(doc.template to()); } // -// JsonError deserializeJson(TDocument& doc, TString json); +// JsonError deserializeJson(TDocument& doc, TChar* input, size_t inputSize); // TDocument = DynamicJsonDocument, StaticJsonDocument -// TString = std::istream&, Stream& -template -JsonError deserializeJson(TDocument &doc, TString &json) { - return Internals::makeParser(&doc.buffer(), json, doc.nestingLimit) +// TChar* = char*, const char*, const FlashStringHelper* +template +JsonError deserializeJson(TDocument &doc, TChar *input, size_t inputSize) { + using namespace Internals; + return makeJsonDeserializer(&doc.buffer(), makeReader(input, inputSize), + makeWriter(doc.buffer(), input), doc.nestingLimit) + .parse(doc.template to()); +} +// +// JsonError deserializeJson(TDocument& doc, TStream input); +// TDocument = DynamicJsonDocument, StaticJsonDocument +// TStream = std::istream&, Stream& +template +JsonError deserializeJson(TDocument &doc, TStream &input) { + using namespace Internals; + return makeJsonDeserializer(&doc.buffer(), makeReader(input), + makeWriter(doc.buffer(), input), doc.nestingLimit) .parse(doc.template to()); } } // namespace ArduinoJson diff --git a/src/ArduinoJson/deserializeMsgPack.hpp b/src/ArduinoJson/deserializeMsgPack.hpp index 42cd83a6..92ac993c 100644 --- a/src/ArduinoJson/deserializeMsgPack.hpp +++ b/src/ArduinoJson/deserializeMsgPack.hpp @@ -5,37 +5,59 @@ #pragma once #include "MsgPack/MsgPackDeserializer.hpp" +#include "Reading/Reader.hpp" +#include "Writing/Writer.hpp" namespace ArduinoJson { -// MsgPackError deserializeMsgPack(TDocument& doc, TString json); -// TDocument = DynamicJsonArray | StaticJsonArray +// MsgPackError deserializeMsgPack(TDocument& doc, TString input); +// TDocument = DynamicJsonDocument, StaticJsonDocument // TString = const std::string&, const String& template typename Internals::EnableIf::value, MsgPackError>::type -deserializeMsgPack(TDocument &doc, const TString &json) { - return Internals::makeMsgPackDeserializer(&doc.buffer(), json, - doc.nestingLimit) +deserializeMsgPack(TDocument &doc, const TString &input) { + using namespace Internals; + return makeMsgPackDeserializer(&doc.buffer(), makeReader(input), + makeWriter(doc.buffer(), input), + doc.nestingLimit) .parse(doc.template to()); } // -// MsgPackError deserializeMsgPack(TDocument& doc, TString json); -// TDocument = DynamicJsonArray | StaticJsonArray -// TString = const char*, const char[N], const FlashStringHelper* -template -MsgPackError deserializeMsgPack(TDocument &doc, TString *json) { - return Internals::makeMsgPackDeserializer(&doc.buffer(), json, - doc.nestingLimit) +// MsgPackError deserializeMsgPack(TDocument& doc, TChar* input); +// TDocument = DynamicJsonDocument, StaticJsonDocument +// TChar* = char*, const char*, const FlashStringHelper* +template +MsgPackError deserializeMsgPack(TDocument &doc, TChar *input) { + using namespace Internals; + return makeMsgPackDeserializer(&doc.buffer(), makeReader(input), + makeWriter(doc.buffer(), input), + doc.nestingLimit) .parse(doc.template to()); } // -// MsgPackError deserializeMsgPack(TDocument& doc, TString json); -// TDocument = DynamicJsonArray | StaticJsonArray -// TString = std::istream&, Stream& -template -MsgPackError deserializeMsgPack(TDocument &doc, TString &json) { - return Internals::makeMsgPackDeserializer(&doc.buffer(), json, - doc.nestingLimit) +// MsgPackError deserializeMsgPack(TDocument& doc, TChar* input, size_t +// inputSize); +// TDocument = DynamicJsonDocument, StaticJsonDocument +// TChar* = char*, const char*, const FlashStringHelper* +template +MsgPackError deserializeMsgPack(TDocument &doc, TChar *input, + size_t inputSize) { + using namespace Internals; + return makeMsgPackDeserializer(&doc.buffer(), makeReader(input, inputSize), + makeWriter(doc.buffer(), input), + doc.nestingLimit) + .parse(doc.template to()); +} +// +// MsgPackError deserializeMsgPack(TDocument& doc, TStream input); +// TDocument = DynamicJsonDocument, StaticJsonDocument +// TStream = std::istream&, Stream& +template +MsgPackError deserializeMsgPack(TDocument &doc, TStream &input) { + using namespace Internals; + return makeMsgPackDeserializer(&doc.buffer(), makeReader(input), + makeWriter(doc.buffer(), input), + doc.nestingLimit) .parse(doc.template to()); } } // namespace ArduinoJson diff --git a/test/JsonArray/CMakeLists.txt b/test/JsonArray/CMakeLists.txt index c0b13b60..d7a5dc09 100644 --- a/test/JsonArray/CMakeLists.txt +++ b/test/JsonArray/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(JsonArrayTests remove.cpp set.cpp size.cpp + std_string.cpp subscript.cpp ) diff --git a/test/JsonArray/std_string.cpp b/test/JsonArray/std_string.cpp new file mode 100644 index 00000000..d6be2973 --- /dev/null +++ b/test/JsonArray/std_string.cpp @@ -0,0 +1,39 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +static void eraseString(std::string &str) { + char *p = const_cast(str.c_str()); + while (*p) *p++ = '*'; +} + +TEST_CASE("std::string") { + DynamicJsonDocument doc; + JsonArray &array = doc.to(); + + SECTION("add()") { + std::string value("hello"); + array.add(value); + eraseString(value); + REQUIRE(std::string("hello") == array[0]); + } + + SECTION("set()") { + std::string value("world"); + array.add("hello"); + array.set(0, value); + eraseString(value); + REQUIRE(std::string("world") == array[0]); + } + + SECTION("operator[]") { + std::string value("world"); + array.add("hello"); + array[0] = value; + eraseString(value); + REQUIRE(std::string("world") == array[0]); + } +} diff --git a/test/JsonDeserializer/CMakeLists.txt b/test/JsonDeserializer/CMakeLists.txt index 6201ea70..16cb2460 100644 --- a/test/JsonDeserializer/CMakeLists.txt +++ b/test/JsonDeserializer/CMakeLists.txt @@ -10,6 +10,8 @@ add_executable(JsonDeserializerTests deserializeJsonValue.cpp JsonError.cpp nestingLimit.cpp + std_istream.cpp + std_string.cpp ) target_link_libraries(JsonDeserializerTests catch) diff --git a/test/JsonDeserializer/JsonError.cpp b/test/JsonDeserializer/JsonError.cpp index edcad063..b80bd66a 100644 --- a/test/JsonDeserializer/JsonError.cpp +++ b/test/JsonDeserializer/JsonError.cpp @@ -25,6 +25,7 @@ TEST_CASE("JsonError") { TEST_STRINGIFICATION(TooDeep); TEST_STRINGIFICATION(NoMemory); TEST_STRINGIFICATION(InvalidInput); + TEST_STRINGIFICATION(IncompleteInput); } SECTION("as boolean") { @@ -32,6 +33,7 @@ TEST_CASE("JsonError") { TEST_BOOLIFICATION(TooDeep, true); TEST_BOOLIFICATION(NoMemory, true); TEST_BOOLIFICATION(InvalidInput, true); + TEST_BOOLIFICATION(IncompleteInput, true); } SECTION("ostream") { diff --git a/test/JsonDeserializer/deserializeJsonArray.cpp b/test/JsonDeserializer/deserializeJsonArray.cpp index 54dcc2d5..57bf5759 100644 --- a/test/JsonDeserializer/deserializeJsonArray.cpp +++ b/test/JsonDeserializer/deserializeJsonArray.cpp @@ -164,13 +164,13 @@ TEST_CASE("deserialize JSON array") { SECTION("Closing single quotes missing") { JsonError err = deserializeJson(doc, "[\"]"); - REQUIRE(err == JsonError::InvalidInput); + REQUIRE(err == JsonError::IncompleteInput); } SECTION("Closing double quotes missing") { JsonError err = deserializeJson(doc, "[\']"); - REQUIRE(err == JsonError::InvalidInput); + REQUIRE(err == JsonError::IncompleteInput); } } @@ -233,21 +233,21 @@ TEST_CASE("deserialize JSON array") { SECTION("/*/") { JsonError err = deserializeJson(doc, "[/*/\n]"); - REQUIRE(err == JsonError::InvalidInput); + REQUIRE(err == JsonError::IncompleteInput); } SECTION("Unfinished comment") { JsonError err = deserializeJson(doc, "[/*COMMENT]"); - REQUIRE(err == JsonError::InvalidInput); + REQUIRE(err == JsonError::IncompleteInput); } SECTION("Final slash missing") { JsonError err = deserializeJson(doc, "[/*COMMENT*]"); - REQUIRE(err == JsonError::InvalidInput); + REQUIRE(err == JsonError::IncompleteInput); } } - SECTION("Line comments") { + SECTION("Trailing comments") { SECTION("Before opening bracket") { JsonError err = deserializeJson(doc, "//COMMENT\n\t[\"hello\"]"); JsonArray& arr = doc.as(); @@ -311,39 +311,53 @@ TEST_CASE("deserialize JSON array") { SECTION("End document with comment") { JsonError err = deserializeJson(doc, "[//COMMENT"); - REQUIRE(err == JsonError::InvalidInput); + REQUIRE(err == JsonError::IncompleteInput); + } + } + + SECTION("Premature null-terminator") { + SECTION("After opening bracket") { + JsonError err = deserializeJson(doc, "["); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("After value") { + JsonError err = deserializeJson(doc, "[1"); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("After comma") { + JsonError err = deserializeJson(doc, "[1,"); + + REQUIRE(err == JsonError::IncompleteInput); + } + } + + SECTION("Premature end of input") { + const char* input = "[1,2]"; + + SECTION("After opening bracket") { + JsonError err = deserializeJson(doc, input, 1); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("After value") { + JsonError err = deserializeJson(doc, input, 2); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("After comma") { + JsonError err = deserializeJson(doc, input, 3); + + REQUIRE(err == JsonError::IncompleteInput); } } SECTION("Misc") { - SECTION("Garbage") { - JsonError err = deserializeJson(doc, "%*$£¤"); - - REQUIRE(err == JsonError::InvalidInput); - } - - SECTION("The opening bracket is missing") { - JsonError err = deserializeJson(doc, "]"); - - REQUIRE(err == JsonError::InvalidInput); - } - - SECTION("The closing bracket is missing") { - JsonError err = deserializeJson(doc, "["); - - REQUIRE(err == JsonError::InvalidInput); - } - - SECTION("Escape sequences") { - JsonError err = - deserializeJson(doc, "[\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"]"); - JsonArray& arr = doc.as(); - - REQUIRE(err == JsonError::Ok); - REQUIRE(1 == arr.size()); - REQUIRE(arr[0] == "1\"2\\3/4\b5\f6\n7\r8\t9"); - } - SECTION("Nested objects") { char jsonString[] = " [ { \"a\" : 1 , \"b\" : 2 } , { \"c\" : 3 , \"d\" : 4 } ] "; diff --git a/test/JsonDeserializer/deserializeJsonArrayStatic.cpp b/test/JsonDeserializer/deserializeJsonArrayStatic.cpp index 45da9c03..8315c7eb 100644 --- a/test/JsonDeserializer/deserializeJsonArrayStatic.cpp +++ b/test/JsonDeserializer/deserializeJsonArrayStatic.cpp @@ -21,7 +21,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { JsonError err = deserializeJson(doc, input); - REQUIRE(err != JsonError::Ok); + REQUIRE(err == JsonError::NoMemory); } SECTION("BufferOfTheRightSizeForArrayWithOneValue") { @@ -39,7 +39,7 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { JsonError err = deserializeJson(doc, input); - REQUIRE(err != JsonError::Ok); + REQUIRE(err == JsonError::NoMemory); } SECTION("BufferOfTheRightSizeForArrayWithNestedObject") { @@ -51,22 +51,6 @@ TEST_CASE("deserialize JSON array with a StaticJsonDocument") { REQUIRE(err == JsonError::Ok); } - SECTION("CharPtrNull") { - StaticJsonDocument<100> doc; - - JsonError err = deserializeJson(doc, static_cast(0)); - - REQUIRE(err != JsonError::Ok); - } - - SECTION("ConstCharPtrNull") { - StaticJsonDocument<100> doc; - - JsonError err = deserializeJson(doc, static_cast(0)); - - REQUIRE(err != JsonError::Ok); - } - SECTION("CopyStringNotSpaces") { StaticJsonDocument<100> doc; diff --git a/test/JsonDeserializer/deserializeJsonObject.cpp b/test/JsonDeserializer/deserializeJsonObject.cpp index f44a8e05..ca848a82 100644 --- a/test/JsonDeserializer/deserializeJsonObject.cpp +++ b/test/JsonDeserializer/deserializeJsonObject.cpp @@ -212,19 +212,39 @@ TEST_CASE("deserialize JSON object") { } } - SECTION("Misc") { - SECTION("The opening brace is missing") { - JsonError err = deserializeJson(doc, "}"); + SECTION("Premature null terminator") { + SECTION("After opening brace") { + JsonError err = deserializeJson(doc, "{"); - REQUIRE(err == JsonError::InvalidInput); + REQUIRE(err == JsonError::IncompleteInput); } - SECTION("The closing brace is missing") { + SECTION("After key") { + JsonError err = deserializeJson(doc, "{\"hello\""); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("After colon") { + JsonError err = deserializeJson(doc, "{\"hello\":"); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("After value") { JsonError err = deserializeJson(doc, "{\"hello\":\"world\""); - REQUIRE(err == JsonError::InvalidInput); + REQUIRE(err == JsonError::IncompleteInput); } + SECTION("After comma") { + JsonError err = deserializeJson(doc, "{\"hello\":\"world\","); + + REQUIRE(err == JsonError::IncompleteInput); + } + } + + SECTION("Misc") { SECTION("A quoted key without value") { JsonError err = deserializeJson(doc, "{\"key\"}"); @@ -250,6 +270,200 @@ TEST_CASE("deserialize JSON object") { } } + SECTION("Block comments") { + SECTION("Before opening brace") { + JsonError err = deserializeJson(doc, "/*COMMENT*/ {\"hello\":\"world\"}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("After opening brace") { + JsonError err = deserializeJson(doc, "{/*COMMENT*/\"hello\":\"world\"}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("Before colon") { + JsonError err = deserializeJson(doc, "{\"hello\"/*COMMENT*/:\"world\"}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("After colon") { + JsonError err = deserializeJson(doc, "{\"hello\":/*COMMENT*/\"world\"}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("Before closing brace") { + JsonError err = deserializeJson(doc, "{\"hello\":\"world\"/*COMMENT*/}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("After closing brace") { + JsonError err = deserializeJson(doc, "{\"hello\":\"world\"}/*COMMENT*/"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("Before comma") { + JsonError err = deserializeJson( + doc, "{\"hello\":\"world\"/*COMMENT*/,\"answer\":42}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + REQUIRE(obj["answer"] == 42); + } + + SECTION("After comma") { + JsonError err = deserializeJson( + doc, "{\"hello\":\"world\",/*COMMENT*/\"answer\":42}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + REQUIRE(obj["answer"] == 42); + } + } + + SECTION("Trailing comments") { + SECTION("Before opening brace") { + JsonError err = deserializeJson(doc, "//COMMENT\n {\"hello\":\"world\"}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("After opening brace") { + JsonError err = deserializeJson(doc, "{//COMMENT\n\"hello\":\"world\"}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("Before colon") { + JsonError err = deserializeJson(doc, "{\"hello\"//COMMENT\n:\"world\"}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("After colon") { + JsonError err = deserializeJson(doc, "{\"hello\"://COMMENT\n\"world\"}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("Before closing brace") { + JsonError err = deserializeJson(doc, "{\"hello\":\"world\"//COMMENT\n}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("After closing brace") { + JsonError err = deserializeJson(doc, "{\"hello\":\"world\"}//COMMENT\n"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("Before comma") { + JsonError err = deserializeJson( + doc, "{\"hello\":\"world\"//COMMENT\n,\"answer\":42}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + REQUIRE(obj["answer"] == 42); + } + + SECTION("After comma") { + JsonError err = deserializeJson( + doc, "{\"hello\":\"world\",//COMMENT\n\"answer\":42}"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + REQUIRE(obj["answer"] == 42); + } + } + + SECTION("Dangling slash") { + SECTION("Before opening brace") { + JsonError err = deserializeJson(doc, "/{\"hello\":\"world\"}"); + + REQUIRE(err == JsonError::InvalidInput); + } + + SECTION("After opening brace") { + JsonError err = deserializeJson(doc, "{/\"hello\":\"world\"}"); + + REQUIRE(err == JsonError::InvalidInput); + } + + SECTION("Before colon") { + JsonError err = deserializeJson(doc, "{\"hello\"/:\"world\"}"); + + REQUIRE(err == JsonError::InvalidInput); + } + + SECTION("After colon") { + JsonError err = deserializeJson(doc, "{\"hello\":/\"world\"}"); + + REQUIRE(err == JsonError::InvalidInput); + } + + SECTION("Before closing brace") { + JsonError err = deserializeJson(doc, "{\"hello\":\"world\"/}"); + + REQUIRE(err == JsonError::InvalidInput); + } + + SECTION("After closing brace") { + JsonError err = deserializeJson(doc, "{\"hello\":\"world\"}/"); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(obj["hello"] == "world"); + } + + SECTION("Before comma") { + JsonError err = + deserializeJson(doc, "{\"hello\":\"world\"/,\"answer\":42}"); + + REQUIRE(err == JsonError::InvalidInput); + } + + SECTION("After comma") { + JsonError err = + deserializeJson(doc, "{\"hello\":\"world\",/\"answer\":42}"); + + REQUIRE(err == JsonError::InvalidInput); + } + } + SECTION("Should clear the JsonObject") { deserializeJson(doc, "{\"hello\":\"world\"}"); deserializeJson(doc, "{}"); diff --git a/test/JsonDeserializer/deserializeJsonObjectStatic.cpp b/test/JsonDeserializer/deserializeJsonObjectStatic.cpp index ab44b2a1..85b2dfef 100644 --- a/test/JsonDeserializer/deserializeJsonObjectStatic.cpp +++ b/test/JsonDeserializer/deserializeJsonObjectStatic.cpp @@ -21,7 +21,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") { JsonError err = deserializeJson(doc, input); - REQUIRE(err != JsonError::Ok); + REQUIRE(err == JsonError::NoMemory); } SECTION("BufferOfTheRightSizeForObjectWithOneValue") { @@ -39,7 +39,7 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") { JsonError err = deserializeJson(doc, input); - REQUIRE(err != JsonError::Ok); + REQUIRE(err == JsonError::NoMemory); } SECTION("BufferOfTheRightSizeForObjectWithNestedObject") { @@ -51,22 +51,6 @@ TEST_CASE("deserialize JSON object with StaticJsonDocument") { REQUIRE(err == JsonError::Ok); } - SECTION("CharPtrNull") { - StaticJsonDocument<100> doc; - - JsonError err = deserializeJson(doc, static_cast(0)); - - REQUIRE(err != JsonError::Ok); - } - - SECTION("ConstCharPtrNull") { - StaticJsonDocument<100> doc; - - JsonError err = deserializeJson(doc, static_cast(0)); - - REQUIRE(err != JsonError::Ok); - } - SECTION("Should clear the JsonObject") { StaticJsonDocument doc; char input[] = "{\"hello\":\"world\"}"; diff --git a/test/JsonDeserializer/deserializeJsonValue.cpp b/test/JsonDeserializer/deserializeJsonValue.cpp index 4d374d5c..fb9f8105 100644 --- a/test/JsonDeserializer/deserializeJsonValue.cpp +++ b/test/JsonDeserializer/deserializeJsonValue.cpp @@ -10,18 +10,16 @@ using namespace Catch::Matchers; TEST_CASE("deserializeJson(DynamicJsonDocument&)") { DynamicJsonDocument doc; - SECTION("EmptyObject") { - JsonError err = deserializeJson(doc, "{}"); + SECTION("null char*") { + JsonError err = deserializeJson(doc, static_cast(0)); - REQUIRE(err == JsonError::Ok); - REQUIRE(doc.is()); + REQUIRE(err != JsonError::Ok); } - SECTION("EmptyArray") { - JsonError err = deserializeJson(doc, "[]"); + SECTION("null const char*") { + JsonError err = deserializeJson(doc, static_cast(0)); - REQUIRE(err == JsonError::Ok); - REQUIRE(doc.is()); + REQUIRE(err != JsonError::Ok); } SECTION("Integer") { @@ -58,6 +56,14 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") { REQUIRE_THAT(doc.as(), Equals("hello world")); } + SECTION("Escape sequences") { + JsonError err = + deserializeJson(doc, "\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\""); + + REQUIRE(err == JsonError::Ok); + REQUIRE(doc.as() == "1\"2\\3/4\b5\f6\n7\r8\t9"); + } + SECTION("True") { JsonError err = deserializeJson(doc, "true"); @@ -74,25 +80,6 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") { REQUIRE(doc.as() == false); } - SECTION("OpenBrace") { - JsonError err = deserializeJson(doc, "{"); - - REQUIRE(err != JsonError::Ok); - } - - SECTION("Incomplete string") { - JsonError err = deserializeJson(doc, "\"hello"); - - REQUIRE(err == JsonError::Ok); - REQUIRE(doc.is()); - REQUIRE_THAT(doc.as(), Equals("hello")); - } - - SECTION("Unterminated escape sequence") { - JsonError err = deserializeJson(doc, "\"\\\0\""); - REQUIRE(err == JsonError::InvalidInput); - } - SECTION("Should clear the JsonVariant") { deserializeJson(doc, "[1,2,3]"); deserializeJson(doc, "{}"); @@ -100,4 +87,86 @@ TEST_CASE("deserializeJson(DynamicJsonDocument&)") { REQUIRE(doc.is()); REQUIRE(doc.memoryUsage() == JSON_OBJECT_SIZE(0)); } + + SECTION("Empty input") { + JsonError err = deserializeJson(doc, ""); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("Just a trailing comment") { + JsonError err = deserializeJson(doc, "// comment"); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("Just a block comment") { + JsonError err = deserializeJson(doc, "/*comment*/"); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("Just a slash") { + JsonError err = deserializeJson(doc, "/"); + + REQUIRE(err == JsonError::InvalidInput); + } + + SECTION("Garbage") { + JsonError err = deserializeJson(doc, "%*$£¤"); + + REQUIRE(err == JsonError::InvalidInput); + } + + SECTION("Premature null-terminator") { + SECTION("In escape sequence") { + JsonError err = deserializeJson(doc, "\"\\"); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("In block comment") { + JsonError err = deserializeJson(doc, "/* comment"); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("In double quoted string") { + JsonError err = deserializeJson(doc, "\"hello"); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("In single quoted string") { + JsonError err = deserializeJson(doc, "'hello"); + + REQUIRE(err == JsonError::IncompleteInput); + } + } + + SECTION("Premature end of input") { + SECTION("In escape sequence") { + JsonError err = deserializeJson(doc, "\"\\n\"", 2); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("In block comment") { + JsonError err = deserializeJson(doc, "/* comment */", 10); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("In double quoted string") { + JsonError err = deserializeJson(doc, "\"hello\"", 6); + + REQUIRE(err == JsonError::IncompleteInput); + } + + SECTION("In single quoted string") { + JsonError err = deserializeJson(doc, "'hello'", 6); + + REQUIRE(err == JsonError::IncompleteInput); + } + } } diff --git a/test/JsonDeserializer/std_istream.cpp b/test/JsonDeserializer/std_istream.cpp new file mode 100644 index 00000000..b1f24218 --- /dev/null +++ b/test/JsonDeserializer/std_istream.cpp @@ -0,0 +1,73 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include +#include + +TEST_CASE("deserializeJson(std::istream&)") { + DynamicJsonDocument doc; + + SECTION("array") { + std::istringstream json(" [ 42 /* comment */ ] "); + + JsonError err = deserializeJson(doc, json); + JsonArray& arr = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(1 == arr.size()); + REQUIRE(42 == arr[0]); + } + + SECTION("object") { + std::istringstream json(" { hello : world // comment\n }"); + + JsonError err = deserializeJson(doc, json); + JsonObject& obj = doc.as(); + + REQUIRE(err == JsonError::Ok); + REQUIRE(1 == obj.size()); + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("Should not read after the closing brace of an empty object") { + std::istringstream json("{}123"); + + deserializeJson(doc, json); + + REQUIRE('1' == char(json.get())); + } + + SECTION("Should not read after the closing brace") { + std::istringstream json("{\"hello\":\"world\"}123"); + + deserializeJson(doc, json); + + REQUIRE('1' == char(json.get())); + } + + SECTION("Should not read after the closing bracket of an empty array") { + std::istringstream json("[]123"); + + deserializeJson(doc, json); + + REQUIRE('1' == char(json.get())); + } + + SECTION("Should not read after the closing bracket") { + std::istringstream json("[\"hello\",\"world\"]123"); + + deserializeJson(doc, json); + + REQUIRE('1' == char(json.get())); + } + + SECTION("Should not read after the closing quote") { + std::istringstream json("\"hello\"123"); + + deserializeJson(doc, json); + + REQUIRE('1' == char(json.get())); + } +} diff --git a/test/JsonDeserializer/std_string.cpp b/test/JsonDeserializer/std_string.cpp new file mode 100644 index 00000000..baa5ee53 --- /dev/null +++ b/test/JsonDeserializer/std_string.cpp @@ -0,0 +1,35 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("deserializeJson(const std::string&)") { + DynamicJsonDocument doc; + + SECTION("should accept const string") { + const std::string input("[42]"); + + JsonError err = deserializeJson(doc, input); + + REQUIRE(err == JsonError::Ok); + } + + SECTION("should accept temporary string") { + JsonError err = deserializeJson(doc, std::string("[42]")); + + REQUIRE(err == JsonError::Ok); + } + + SECTION("should duplicate content") { + std::string input("[\"hello\"]"); + + JsonError err = deserializeJson(doc, input); + input[2] = 'X'; // alter the string tomake sure we made a copy + + JsonArray &array = doc.as(); + REQUIRE(err == JsonError::Ok); + REQUIRE(std::string("hello") == array[0]); + } +} diff --git a/test/JsonObject/CMakeLists.txt b/test/JsonObject/CMakeLists.txt index b6de6d30..5cb25efa 100644 --- a/test/JsonObject/CMakeLists.txt +++ b/test/JsonObject/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(JsonObjectTests remove.cpp set.cpp size.cpp + std_string.cpp subscript.cpp ) diff --git a/test/JsonObject/std_string.cpp b/test/JsonObject/std_string.cpp new file mode 100644 index 00000000..accd7ae1 --- /dev/null +++ b/test/JsonObject/std_string.cpp @@ -0,0 +1,173 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +static void eraseString(std::string &str) { + char *p = const_cast(str.c_str()); + while (*p) *p++ = '*'; +} + +TEST_CASE("std::string") { + DynamicJsonDocument doc; + + SECTION("operator[]") { + char json[] = "{\"key\":\"value\"}"; + + deserializeJson(doc, json); + JsonObject &obj = doc.as(); + + REQUIRE(std::string("value") == obj[std::string("key")]); + } + + SECTION("operator[] const") { + char json[] = "{\"key\":\"value\"}"; + + deserializeJson(doc, json); + JsonObject &obj = doc.as(); + + REQUIRE(std::string("value") == obj[std::string("key")]); + } + + SECTION("set(key)") { + JsonObject &obj = doc.to(); + std::string key("hello"); + obj.set(key, "world"); + eraseString(key); + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("set(value)") { + JsonObject &obj = doc.to(); + std::string value("world"); + obj.set("hello", value); + eraseString(value); + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("set(key,value)") { + JsonObject &obj = doc.to(); + std::string key("hello"); + std::string value("world"); + obj.set(key, value); + eraseString(key); + eraseString(value); + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("set(JsonArraySubscript)") { + JsonObject &obj = doc.to(); + DynamicJsonDocument doc2; + JsonArray &arr = doc2.to(); + arr.add("world"); + + obj.set(std::string("hello"), arr[0]); + + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("set(JsonObjectSubscript)") { + JsonObject &obj = doc.to(); + DynamicJsonDocument doc2; + JsonObject &obj2 = doc2.to(); + obj2.set("x", "world"); + + obj.set(std::string("hello"), obj2["x"]); + + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("get()") { + char json[] = "{\"key\":\"value\"}"; + deserializeJson(doc, json); + JsonObject &obj = doc.as(); + + REQUIRE(std::string("value") == obj.get(std::string("key"))); + } + + SECTION("is()") { + char json[] = "{\"key\":\"value\"}"; + deserializeJson(doc, json); + JsonObject &obj = doc.as(); + + REQUIRE(true == obj.is(std::string("key"))); + } + + SECTION("createNestedObject()") { + JsonObject &obj = doc.to(); + std::string key = "key"; + char json[64]; + obj.createNestedObject(key); + eraseString(key); + serializeJson(doc, json, sizeof(json)); + REQUIRE(std::string("{\"key\":{}}") == json); + } + + SECTION("createNestedArray()") { + JsonObject &obj = doc.to(); + std::string key = "key"; + char json[64]; + obj.createNestedArray(key); + eraseString(key); + serializeJson(doc, json, sizeof(json)); + REQUIRE(std::string("{\"key\":[]}") == json); + } + + SECTION("containsKey()") { + char json[] = "{\"key\":\"value\"}"; + deserializeJson(doc, json); + JsonObject &obj = doc.as(); + REQUIRE(true == obj.containsKey(std::string("key"))); + } + + SECTION("remove()") { + JsonObject &obj = doc.to(); + obj["key"] = "value"; + + obj.remove(std::string("key")); + + REQUIRE(0 == obj.size()); + } + + SECTION("operator[], set key") { + std::string key("hello"); + JsonObject &obj = doc.to(); + obj[key] = "world"; + eraseString(key); + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("operator[], set value") { + std::string value("world"); + JsonObject &obj = doc.to(); + obj["hello"] = value; + eraseString(value); + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("memoryUsage() increases when adding a new key") { + std::string key1("hello"), key2("world"); + JsonObject &obj = doc.to(); + + obj[key1] = 1; + size_t sizeBefore = doc.memoryUsage(); + obj[key2] = 2; + size_t sizeAfter = doc.memoryUsage(); + + REQUIRE(sizeAfter - sizeBefore >= key2.size()); + } + + SECTION("memoryUsage() remains when adding the same key") { + std::string key("hello"); + JsonObject &obj = doc.to(); + + obj[key] = 1; + size_t sizeBefore = doc.memoryUsage(); + obj[key] = 2; + size_t sizeAfter = doc.memoryUsage(); + + REQUIRE(sizeBefore == sizeAfter); + } +} diff --git a/test/JsonSerializer/CMakeLists.txt b/test/JsonSerializer/CMakeLists.txt index f513ca1d..1dc605dc 100644 --- a/test/JsonSerializer/CMakeLists.txt +++ b/test/JsonSerializer/CMakeLists.txt @@ -8,6 +8,8 @@ add_executable(JsonSerializerTests JsonObject.cpp JsonObjectPretty.cpp JsonVariant.cpp + std_stream.cpp + std_string.cpp ) target_link_libraries(JsonSerializerTests catch) diff --git a/test/JsonSerializer/std_stream.cpp b/test/JsonSerializer/std_stream.cpp new file mode 100644 index 00000000..0d52846a --- /dev/null +++ b/test/JsonSerializer/std_stream.cpp @@ -0,0 +1,64 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include +#include + +TEST_CASE("operator<<(std::ostream)") { + DynamicJsonDocument doc; + std::ostringstream os; + + SECTION("JsonVariant containing false") { + JsonVariant variant = false; + + os << variant; + + REQUIRE("false" == os.str()); + } + + SECTION("JsonVariant containing string") { + JsonVariant variant = "coucou"; + + os << variant; + + REQUIRE("\"coucou\"" == os.str()); + } + + SECTION("JsonObject") { + JsonObject& object = doc.to(); + object["key"] = "value"; + + os << object; + + REQUIRE("{\"key\":\"value\"}" == os.str()); + } + + SECTION("JsonObjectSubscript") { + JsonObject& object = doc.to(); + object["key"] = "value"; + + os << object["key"]; + + REQUIRE("\"value\"" == os.str()); + } + + SECTION("JsonArray") { + JsonArray& array = doc.to(); + array.add("value"); + + os << array; + + REQUIRE("[\"value\"]" == os.str()); + } + + SECTION("JsonArraySubscript") { + JsonArray& array = doc.to(); + array.add("value"); + + os << array[0]; + + REQUIRE("\"value\"" == os.str()); + } +} diff --git a/test/JsonSerializer/std_string.cpp b/test/JsonSerializer/std_string.cpp new file mode 100644 index 00000000..2d9608a4 --- /dev/null +++ b/test/JsonSerializer/std_string.cpp @@ -0,0 +1,47 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("serialize JsonArray to std::string") { + DynamicJsonDocument doc; + JsonArray &array = doc.to(); + array.add(4); + array.add(2); + + SECTION("serializeJson()") { + std::string json; + serializeJson(array, json); + + REQUIRE(std::string("[4,2]") == json); + } + + SECTION("serializeJsonPretty") { + std::string json; + serializeJsonPretty(array, json); + + REQUIRE(std::string("[\r\n 4,\r\n 2\r\n]") == json); + } +} + +TEST_CASE("serialize JsonObject to std::string") { + DynamicJsonDocument doc; + JsonObject &obj = doc.to(); + obj["key"] = "value"; + + SECTION("object") { + std::string json; + serializeJson(doc, json); + + REQUIRE(std::string("{\"key\":\"value\"}") == json); + } + + SECTION("serializeJsonPretty") { + std::string json; + serializeJsonPretty(doc, json); + + REQUIRE(std::string("{\r\n \"key\": \"value\"\r\n}") == json); + } +} diff --git a/test/Misc/CMakeLists.txt b/test/Misc/CMakeLists.txt index 28e273d0..69db9af7 100644 --- a/test/Misc/CMakeLists.txt +++ b/test/Misc/CMakeLists.txt @@ -2,10 +2,8 @@ # Copyright Benoit Blanchon 2014-2018 # MIT License -add_executable(MiscTests +add_executable(MiscTests FloatParts.cpp - std_stream.cpp - std_string.cpp StringBuilder.cpp StringTraits.cpp TypeTraits.cpp diff --git a/test/Misc/std_stream.cpp b/test/Misc/std_stream.cpp deleted file mode 100644 index 5ac6701f..00000000 --- a/test/Misc/std_stream.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#include -#include -#include - -TEST_CASE("std::stream") { - SECTION("JsonVariantFalse") { - std::ostringstream os; - JsonVariant variant = false; - os << variant; - REQUIRE("false" == os.str()); - } - - SECTION("JsonVariantString") { - std::ostringstream os; - JsonVariant variant = "coucou"; - os << variant; - REQUIRE("\"coucou\"" == os.str()); - } - - SECTION("JsonObject") { - std::ostringstream os; - DynamicJsonDocument doc; - JsonObject& object = doc.to(); - object["key"] = "value"; - os << object; - REQUIRE("{\"key\":\"value\"}" == os.str()); - } - - SECTION("JsonObjectSubscript") { - std::ostringstream os; - DynamicJsonDocument doc; - JsonObject& object = doc.to(); - object["key"] = "value"; - os << object["key"]; - REQUIRE("\"value\"" == os.str()); - } - - SECTION("JsonArray") { - std::ostringstream os; - DynamicJsonDocument doc; - JsonArray& array = doc.to(); - array.add("value"); - os << array; - REQUIRE("[\"value\"]" == os.str()); - } - - SECTION("JsonArraySubscript") { - std::ostringstream os; - DynamicJsonDocument doc; - JsonArray& array = doc.to(); - array.add("value"); - os << array[0]; - REQUIRE("\"value\"" == os.str()); - } - - SECTION("ParseArray") { - std::istringstream json(" [ 42 /* comment */ ] "); - DynamicJsonDocument doc; - JsonError err = deserializeJson(doc, json); - JsonArray& arr = doc.as(); - - REQUIRE(err == JsonError::Ok); - REQUIRE(1 == arr.size()); - REQUIRE(42 == arr[0]); - } - - SECTION("ParseObject") { - std::istringstream json(" { hello : world // comment\n }"); - DynamicJsonDocument doc; - JsonError err = deserializeJson(doc, json); - JsonObject& obj = doc.as(); - - REQUIRE(err == JsonError::Ok); - REQUIRE(1 == obj.size()); - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("ShouldNotReadPastTheEnd") { - std::istringstream json("{}123"); - DynamicJsonDocument doc; - deserializeJson(doc, json); - REQUIRE('1' == json.get()); - } -} diff --git a/test/Misc/std_string.cpp b/test/Misc/std_string.cpp deleted file mode 100644 index 4765f707..00000000 --- a/test/Misc/std_string.cpp +++ /dev/null @@ -1,259 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#include -#include - -static void eraseString(std::string &str) { - char *p = const_cast(str.c_str()); - while (*p) *p++ = '*'; -} - -TEST_CASE("std::string") { - SECTION("deserializeJson duplicates content") { - std::string json("[\"hello\"]"); - - DynamicJsonDocument doc; - JsonError err = deserializeJson(doc, json); - eraseString(json); - - JsonArray &array = doc.as(); - REQUIRE(err == JsonError::Ok); - REQUIRE(std::string("hello") == array[0]); - } - - SECTION("JsonArray") { - DynamicJsonDocument doc; - JsonArray &array = doc.to(); - - SECTION("add()") { - std::string value("hello"); - array.add(value); - eraseString(value); - REQUIRE(std::string("hello") == array[0]); - } - - SECTION("set()") { - std::string value("world"); - array.add("hello"); - array.set(0, value); - eraseString(value); - REQUIRE(std::string("world") == array[0]); - } - - SECTION("operator[]") { - std::string value("world"); - array.add("hello"); - array[0] = value; - eraseString(value); - REQUIRE(std::string("world") == array[0]); - } - - SECTION("serializeJson()") { - array.add(4); - array.add(2); - std::string json; - serializeJson(array, json); - REQUIRE(std::string("[4,2]") == json); - } - - SECTION("serializeJsonPretty()") { - array.add(4); - array.add(2); - std::string json; - serializeJsonPretty(array, json); - REQUIRE(std::string("[\r\n 4,\r\n 2\r\n]") == json); - } - } - - SECTION("JsonObject") { - DynamicJsonDocument doc; - - SECTION("deserializeJson()") { - std::string json("{\"hello\":\"world\"}"); - - JsonError err = deserializeJson(doc, json); - JsonObject &obj = doc.as(); - eraseString(json); - - REQUIRE(err == JsonError::Ok); - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("operator[]") { - char json[] = "{\"key\":\"value\"}"; - - deserializeJson(doc, json); - JsonObject &obj = doc.as(); - - REQUIRE(std::string("value") == obj[std::string("key")]); - } - - SECTION("operator[] const") { - char json[] = "{\"key\":\"value\"}"; - - deserializeJson(doc, json); - JsonObject &obj = doc.as(); - - REQUIRE(std::string("value") == obj[std::string("key")]); - } - - SECTION("set(key)") { - JsonObject &obj = doc.to(); - std::string key("hello"); - obj.set(key, "world"); - eraseString(key); - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("set(value)") { - JsonObject &obj = doc.to(); - std::string value("world"); - obj.set("hello", value); - eraseString(value); - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("set(key,value)") { - JsonObject &obj = doc.to(); - std::string key("hello"); - std::string value("world"); - obj.set(key, value); - eraseString(key); - eraseString(value); - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("set(JsonArraySubscript)") { - JsonObject &obj = doc.to(); - DynamicJsonDocument doc2; - JsonArray &arr = doc2.to(); - arr.add("world"); - - obj.set(std::string("hello"), arr[0]); - - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("set(JsonObjectSubscript)") { - JsonObject &obj = doc.to(); - DynamicJsonDocument doc2; - JsonObject &obj2 = doc2.to(); - obj2.set("x", "world"); - - obj.set(std::string("hello"), obj2["x"]); - - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("get()") { - char json[] = "{\"key\":\"value\"}"; - deserializeJson(doc, json); - JsonObject &obj = doc.as(); - - REQUIRE(std::string("value") == - obj.get(std::string("key"))); - } - - SECTION("is()") { - char json[] = "{\"key\":\"value\"}"; - deserializeJson(doc, json); - JsonObject &obj = doc.as(); - - REQUIRE(true == obj.is(std::string("key"))); - } - - SECTION("createNestedObject()") { - JsonObject &obj = doc.to(); - std::string key = "key"; - char json[64]; - obj.createNestedObject(key); - eraseString(key); - serializeJson(doc, json, sizeof(json)); - REQUIRE(std::string("{\"key\":{}}") == json); - } - - SECTION("createNestedArray()") { - JsonObject &obj = doc.to(); - std::string key = "key"; - char json[64]; - obj.createNestedArray(key); - eraseString(key); - serializeJson(doc, json, sizeof(json)); - REQUIRE(std::string("{\"key\":[]}") == json); - } - - SECTION("containsKey()") { - char json[] = "{\"key\":\"value\"}"; - deserializeJson(doc, json); - JsonObject &obj = doc.as(); - REQUIRE(true == obj.containsKey(std::string("key"))); - } - - SECTION("remove()") { - JsonObject &obj = doc.to(); - obj["key"] = "value"; - - obj.remove(std::string("key")); - - REQUIRE(0 == obj.size()); - } - - SECTION("operator[], set key") { - std::string key("hello"); - JsonObject &obj = doc.to(); - obj[key] = "world"; - eraseString(key); - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("operator[], set value") { - std::string value("world"); - JsonObject &obj = doc.to(); - obj["hello"] = value; - eraseString(value); - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("serializeJson()") { - JsonObject &obj = doc.to(); - obj["key"] = "value"; - std::string json; - serializeJson(doc, json); - REQUIRE(std::string("{\"key\":\"value\"}") == json); - } - - SECTION("serializeJsonPretty()") { - JsonObject &obj = doc.to(); - obj["key"] = "value"; - std::string json; - serializeJsonPretty(doc, json); - REQUIRE(std::string("{\r\n \"key\": \"value\"\r\n}") == json); - } - - SECTION("memoryUsage() increases when adding a new key") { - std::string key1("hello"), key2("world"); - JsonObject &obj = doc.to(); - - obj[key1] = 1; - size_t sizeBefore = doc.memoryUsage(); - obj[key2] = 2; - size_t sizeAfter = doc.memoryUsage(); - - REQUIRE(sizeAfter - sizeBefore >= key2.size()); - } - - SECTION("memoryUsage() remains when adding the same key") { - std::string key("hello"); - JsonObject &obj = doc.to(); - - obj[key] = 1; - size_t sizeBefore = doc.memoryUsage(); - obj[key] = 2; - size_t sizeAfter = doc.memoryUsage(); - - REQUIRE(sizeBefore == sizeAfter); - } - } -} diff --git a/test/Misc/unsigned_char.cpp b/test/Misc/unsigned_char.cpp index 87f3cab6..4471e676 100644 --- a/test/Misc/unsigned_char.cpp +++ b/test/Misc/unsigned_char.cpp @@ -9,259 +9,267 @@ #define CONFLICTS_WITH_BUILTIN_OPERATOR #endif -TEST_CASE("unsigned char string") { - SECTION("JsonBuffer::parseArray") { - unsigned char json[] = "[42]"; - - StaticJsonDocument doc; - JsonError err = deserializeJson(doc, json); - - REQUIRE(err == JsonError::Ok); - } - - SECTION("JsonBuffer::parseObject") { - unsigned char json[] = "{\"a\":42}"; +TEST_CASE("unsigned char[]") { + SECTION("deserializeJson()") { + unsigned char input[] = "{\"a\":42}"; StaticJsonDocument doc; - JsonError err = deserializeJson(doc, json); + JsonError err = deserializeJson(doc, input); REQUIRE(err == JsonError::Ok); } - SECTION("JsonVariant constructor") { - unsigned char value[] = "42"; + SECTION("deserializeMsgPack()") { + unsigned char input[] = "\xDE\x00\x01\xA5Hello\xA5world"; - JsonVariant variant(value); + StaticJsonDocument doc; + MsgPackError err = deserializeMsgPack(doc, input); - REQUIRE(42 == variant.as()); + REQUIRE(err == MsgPackError::Ok); } - SECTION("JsonVariant assignment operator") { - unsigned char value[] = "42"; + SECTION("JsonVariant") { + SECTION("constructor") { + unsigned char value[] = "42"; - JsonVariant variant(666); - variant = value; + JsonVariant variant(value); - REQUIRE(42 == variant.as()); - } + REQUIRE(42 == variant.as()); + } + + SECTION("operator=") { + unsigned char value[] = "42"; + + JsonVariant variant(666); + variant = value; + + REQUIRE(42 == variant.as()); + } #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR - SECTION("JsonVariant::operator[]") { - unsigned char key[] = "hello"; + SECTION("operator[]") { + unsigned char key[] = "hello"; - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); - JsonVariant variant = doc.as(); + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + JsonVariant variant = doc.as(); - REQUIRE(std::string("world") == variant[key]); - } + REQUIRE(std::string("world") == variant[key]); + } #endif #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR - SECTION("JsonVariant::operator[] const") { - unsigned char key[] = "hello"; + SECTION("operator[] const") { + unsigned char key[] = "hello"; - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); - const JsonVariant variant = doc.as(); + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + const JsonVariant variant = doc.as(); - REQUIRE(std::string("world") == variant[key]); - } + REQUIRE(std::string("world") == variant[key]); + } #endif - SECTION("JsonVariant::operator==") { - unsigned char comparand[] = "hello"; + SECTION("operator==") { + unsigned char comparand[] = "hello"; - JsonVariant variant; - variant = "hello"; + JsonVariant variant; + variant = "hello"; - REQUIRE(comparand == variant); - REQUIRE(variant == comparand); - REQUIRE_FALSE(comparand != variant); - REQUIRE_FALSE(variant != comparand); - } - - SECTION("JsonVariant::operator!=") { - unsigned char comparand[] = "hello"; - - JsonVariant variant; - variant = "world"; - - REQUIRE(comparand != variant); - REQUIRE(variant != comparand); - REQUIRE_FALSE(comparand == variant); - REQUIRE_FALSE(variant == comparand); + REQUIRE(comparand == variant); + REQUIRE(variant == comparand); + REQUIRE_FALSE(comparand != variant); + REQUIRE_FALSE(variant != comparand); + } + + SECTION("operator!=") { + unsigned char comparand[] = "hello"; + + JsonVariant variant; + variant = "world"; + + REQUIRE(comparand != variant); + REQUIRE(variant != comparand); + REQUIRE_FALSE(comparand == variant); + REQUIRE_FALSE(variant == comparand); + } } + SECTION("JsonObject") { #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR - SECTION("JsonObject::operator[]") { - unsigned char key[] = "hello"; + SECTION("operator[]") { + unsigned char key[] = "hello"; - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj[key] = "world"; + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj[key] = "world"; - REQUIRE(std::string("world") == obj["hello"]); - } + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("JsonObject::operator[] const") { + unsigned char key[] = "hello"; + + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + + JsonObject& obj = doc.as(); + REQUIRE(std::string("world") == obj[key]); + } #endif - SECTION("JsonObjectSubscript::operator=") { // issue #416 - unsigned char value[] = "world"; + SECTION("get()") { + unsigned char key[] = "hello"; - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj["hello"] = value; + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + JsonObject& obj = doc.as(); + REQUIRE(std::string("world") == obj.get(key)); + } - REQUIRE(std::string("world") == obj["hello"]); + SECTION("set() key") { + unsigned char key[] = "hello"; + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.set(key, "world"); + + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("set() value") { + unsigned char value[] = "world"; + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.set("hello", value); + + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("set() key&value") { + unsigned char key[] = "world"; + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.set(key, key); + + REQUIRE(std::string("world") == obj["world"]); + } + + SECTION("containsKey()") { + unsigned char key[] = "hello"; + + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + JsonObject& obj = doc.as(); + REQUIRE(true == obj.containsKey(key)); + } + + SECTION("remove()") { + unsigned char key[] = "hello"; + + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + JsonObject& obj = doc.as(); + obj.remove(key); + + REQUIRE(0 == obj.size()); + } + + SECTION("is()") { + unsigned char key[] = "hello"; + + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":42}"); + JsonObject& obj = doc.as(); + + REQUIRE(true == obj.is(key)); + } + + SECTION("createNestedArray()") { + unsigned char key[] = "hello"; + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.createNestedArray(key); + } + + SECTION("createNestedObject()") { + unsigned char key[] = "hello"; + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.createNestedObject(key); + } } - SECTION("JsonObjectSubscript::set()") { - unsigned char value[] = "world"; + SECTION("JsonObjectSubscript") { + SECTION("operator=") { // issue #416 + unsigned char value[] = "world"; - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj["hello"].set(value); + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj["hello"] = value; - REQUIRE(std::string("world") == obj["hello"]); + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("set()") { + unsigned char value[] = "world"; + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj["hello"].set(value); + + REQUIRE(std::string("world") == obj["hello"]); + } } -#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR - SECTION("JsonObject::operator[] const") { - unsigned char key[] = "hello"; + SECTION("JsonArray") { + SECTION("add()") { + unsigned char value[] = "world"; - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); + DynamicJsonDocument doc; + JsonArray& arr = doc.to(); + arr.add(value); - JsonObject& obj = doc.as(); - REQUIRE(std::string("world") == obj[key]); - } -#endif + REQUIRE(std::string("world") == arr[0]); + } - SECTION("JsonObject::get()") { - unsigned char key[] = "hello"; + SECTION("set()") { + unsigned char value[] = "world"; - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); - JsonObject& obj = doc.as(); - REQUIRE(std::string("world") == obj.get(key)); + DynamicJsonDocument doc; + JsonArray& arr = doc.to(); + arr.add("hello"); + arr.set(0, value); + + REQUIRE(std::string("world") == arr[0]); + } } - SECTION("JsonObject::set() key") { - unsigned char key[] = "hello"; + SECTION("JsonArraySubscript") { + SECTION("set()") { + unsigned char value[] = "world"; - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.set(key, "world"); + DynamicJsonDocument doc; + JsonArray& arr = doc.to(); + arr.add("hello"); + arr[0].set(value); - REQUIRE(std::string("world") == obj["hello"]); - } + REQUIRE(std::string("world") == arr[0]); + } - SECTION("JsonObject::set() value") { - unsigned char value[] = "world"; + SECTION("operator=") { + unsigned char value[] = "world"; - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.set("hello", value); + DynamicJsonDocument doc; + JsonArray& arr = doc.to(); + arr.add("hello"); + arr[0] = value; - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("JsonObject::set key&value") { - unsigned char key[] = "world"; - - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.set(key, key); - - REQUIRE(std::string("world") == obj["world"]); - } - - SECTION("JsonObject::containsKey()") { - unsigned char key[] = "hello"; - - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); - JsonObject& obj = doc.as(); - REQUIRE(true == obj.containsKey(key)); - } - - SECTION("JsonObject::remove()") { - unsigned char key[] = "hello"; - - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); - JsonObject& obj = doc.as(); - obj.remove(key); - - REQUIRE(0 == obj.size()); - } - - SECTION("JsonObject::is()") { - unsigned char key[] = "hello"; - - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":42}"); - JsonObject& obj = doc.as(); - - REQUIRE(true == obj.is(key)); - } - - SECTION("JsonObject::createNestedArray()") { - unsigned char key[] = "hello"; - - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.createNestedArray(key); - } - - SECTION("JsonObject::createNestedObject()") { - unsigned char key[] = "hello"; - - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.createNestedObject(key); - } - - SECTION("JsonArray::add()") { - unsigned char value[] = "world"; - - DynamicJsonDocument doc; - JsonArray& arr = doc.to(); - arr.add(value); - - REQUIRE(std::string("world") == arr[0]); - } - - SECTION("JsonArray::set()") { - unsigned char value[] = "world"; - - DynamicJsonDocument doc; - JsonArray& arr = doc.to(); - arr.add("hello"); - arr.set(0, value); - - REQUIRE(std::string("world") == arr[0]); - } - - SECTION("JsonArraySubscript::set()") { - unsigned char value[] = "world"; - - DynamicJsonDocument doc; - JsonArray& arr = doc.to(); - arr.add("hello"); - arr[0].set(value); - - REQUIRE(std::string("world") == arr[0]); - } - - SECTION("JsonArraySubscript::operator=") { - unsigned char value[] = "world"; - - DynamicJsonDocument doc; - JsonArray& arr = doc.to(); - arr.add("hello"); - arr[0] = value; - - REQUIRE(std::string("world") == arr[0]); + REQUIRE(std::string("world") == arr[0]); + } } } diff --git a/test/Misc/vla.cpp b/test/Misc/vla.cpp index 2fd84453..89207c89 100644 --- a/test/Misc/vla.cpp +++ b/test/Misc/vla.cpp @@ -17,322 +17,321 @@ #ifndef VLA_NOT_SUPPORTED TEST_CASE("Variable Length Array") { - SECTION("ParseArray") { - int i = 8; + SECTION("deserializeJson()") { + int i = 9; char vla[i]; - strcpy(vla, "[42]"); + strcpy(vla, "{\"a\":42}"); - StaticJsonDocument doc; + StaticJsonDocument doc; JsonError err = deserializeJson(doc, vla); REQUIRE(err == JsonError::Ok); } - SECTION("ParseObject") { + SECTION("deserializeMsgPack()") { int i = 16; char vla[i]; - strcpy(vla, "{\"a\":42}"); + memcpy(vla, "\xDE\x00\x01\xA5Hello\xA5world", 15); StaticJsonDocument doc; - JsonError error = deserializeJson(doc, vla); + MsgPackError err = deserializeMsgPack(doc, vla); - REQUIRE(error == JsonError::Ok); + REQUIRE(err == MsgPackError::Ok); } - SECTION("Parse") { - int i = 16; - char vla[i]; - strcpy(vla, "42"); + SECTION("JsonVariant") { + SECTION("constructor") { + int i = 16; + char vla[i]; + strcpy(vla, "42"); - StaticJsonDocument<> variant; - deserializeJson(variant, vla); + JsonVariant variant(vla); - REQUIRE(42 == variant.as()); - } + REQUIRE(42 == variant.as()); + } - SECTION("JsonVariant_Constructor") { - int i = 16; - char vla[i]; - strcpy(vla, "42"); + SECTION("operator=") { + int i = 16; + char vla[i]; + strcpy(vla, "42"); - JsonVariant variant(vla); + JsonVariant variant(666); + variant = vla; - REQUIRE(42 == variant.as()); - } - - SECTION("JsonVariant_Assign") { - int i = 16; - char vla[i]; - strcpy(vla, "42"); - - JsonVariant variant(666); - variant = vla; - - REQUIRE(42 == variant.as()); - } + REQUIRE(42 == variant.as()); + } #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR - SECTION("JsonVariant_Subscript") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); + SECTION("operator[]") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); - JsonVariant variant = doc.as(); + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + JsonVariant variant = doc.as(); - REQUIRE(std::string("world") == variant[vla]); - } + REQUIRE(std::string("world") == variant[vla]); + } #endif #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR - SECTION("JsonVariant_Subscript_Const") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); + SECTION("operator[] const") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); - const JsonVariant variant = doc.as(); + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + const JsonVariant variant = doc.as(); - REQUIRE(std::string("world") == variant[vla]); - } + REQUIRE(std::string("world") == variant[vla]); + } #endif - SECTION("JsonVariant_Equals") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); + SECTION("operator==") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); - JsonVariant variant; - variant = "hello"; + JsonVariant variant; + variant = "hello"; - REQUIRE((vla == variant)); - REQUIRE((variant == vla)); - REQUIRE_FALSE((vla != variant)); - REQUIRE_FALSE((variant != vla)); + REQUIRE((vla == variant)); + REQUIRE((variant == vla)); + REQUIRE_FALSE((vla != variant)); + REQUIRE_FALSE((variant != vla)); + } + + SECTION("operator!=") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + JsonVariant variant; + variant = "world"; + + REQUIRE((vla != variant)); + REQUIRE((variant != vla)); + REQUIRE_FALSE((vla == variant)); + REQUIRE_FALSE((variant == vla)); + } } - SECTION("JsonVariant_Differs") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); + SECTION("JsonObject") { +#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR + SECTION("operator[]") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); - JsonVariant variant; - variant = "world"; + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj[vla] = "world"; - REQUIRE((vla != variant)); - REQUIRE((variant != vla)); - REQUIRE_FALSE((vla == variant)); - REQUIRE_FALSE((variant == vla)); - } + REQUIRE(std::string("world") == obj["hello"]); + } +#endif #ifndef CONFLICTS_WITH_BUILTIN_OPERATOR - SECTION("JsonObject_Subscript") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); + SECTION("operator[] const") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj[vla] = "world"; + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); - REQUIRE(std::string("world") == obj["hello"]); - } + JsonObject& obj = doc.as(); + REQUIRE(std::string("world") == obj[vla]); + } #endif - SECTION("JsonObject_Subscript_Assign") { // issue #416 - int i = 32; - char vla[i]; - strcpy(vla, "world"); + SECTION("get()") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj["hello"] = vla; + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); - REQUIRE(std::string("world") == obj["hello"].as()); + JsonObject& obj = doc.as(); + REQUIRE(std::string("world") == obj.get(vla)); + } + + SECTION("set() key") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.set(vla, "world"); + + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("set() value") { + int i = 16; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.set("hello", vla); + + REQUIRE(std::string("world") == obj["hello"]); + } + + SECTION("set() key&value") { + int i = 16; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.set(vla, vla); + + REQUIRE(std::string("world") == obj["world"]); + } + + SECTION("containsKey()") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + + JsonObject& obj = doc.as(); + REQUIRE(true == obj.containsKey(vla)); + } + + SECTION("remove()") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":\"world\"}"); + JsonObject& obj = doc.as(); + obj.remove(vla); + + REQUIRE(0 == obj.size()); + } + + SECTION("is()") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonDocument doc; + deserializeJson(doc, "{\"hello\":42}"); + JsonObject& obj = doc.as(); + + REQUIRE(true == obj.is(vla)); + } + + SECTION("createNestedArray()") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.createNestedArray(vla); + } + + SECTION("createNestedObject()") { + int i = 16; + char vla[i]; + strcpy(vla, "hello"); + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj.createNestedObject(vla); + } } - SECTION("JsonObject_Subscript_Set") { - int i = 32; - char vla[i]; - strcpy(vla, "world"); + SECTION("JsonObjectSubscript") { + SECTION("operator=") { // issue #416 + int i = 32; + char vla[i]; + strcpy(vla, "world"); - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj["hello"].set(vla); + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj["hello"] = vla; - REQUIRE(std::string("world") == obj["hello"].as()); + REQUIRE(std::string("world") == obj["hello"].as()); + } + + SECTION("set()") { + int i = 32; + char vla[i]; + strcpy(vla, "world"); + + DynamicJsonDocument doc; + JsonObject& obj = doc.to(); + obj["hello"].set(vla); + + REQUIRE(std::string("world") == obj["hello"].as()); + } } -#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR - SECTION("JsonObject_Subscript_Const") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); + SECTION("JsonArray") { + SECTION("add()") { + int i = 16; + char vla[i]; + strcpy(vla, "world"); - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); + DynamicJsonDocument doc; + JsonArray& arr = doc.to(); + arr.add(vla); - JsonObject& obj = doc.as(); - REQUIRE(std::string("world") == obj[vla]); - } -#endif + REQUIRE(std::string("world") == arr[0]); + } - SECTION("JsonObject_Get") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); + SECTION("set()") { + int i = 16; + char vla[i]; + strcpy(vla, "world"); - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); + DynamicJsonDocument doc; + JsonArray& arr = doc.to(); + arr.add("hello"); + arr.set(0, vla); - JsonObject& obj = doc.as(); - REQUIRE(std::string("world") == obj.get(vla)); + REQUIRE(std::string("world") == arr[0]); + } } - SECTION("JsonObject_Set_Key") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); + SECTION("JsonArraySubscript") { + SECTION("set()") { + int i = 16; + char vla[i]; + strcpy(vla, "world"); - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.set(vla, "world"); + DynamicJsonDocument doc; + JsonArray& arr = doc.to(); + arr.add("hello"); + arr[0].set(vla); - REQUIRE(std::string("world") == obj["hello"]); - } + REQUIRE(std::string("world") == arr[0]); + } - SECTION("JsonObject_Set_Value") { - int i = 16; - char vla[i]; - strcpy(vla, "world"); + SECTION("operator=") { + int i = 16; + char vla[i]; + strcpy(vla, "world"); - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.set("hello", vla); + DynamicJsonDocument doc; + JsonArray& arr = doc.to(); + arr.add("hello"); + arr[0] = vla; - REQUIRE(std::string("world") == obj["hello"]); - } - - SECTION("JsonObject_Set_KeyAndValue") { - int i = 16; - char vla[i]; - strcpy(vla, "world"); - - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.set(vla, vla); - - REQUIRE(std::string("world") == obj["world"]); - } - - SECTION("JsonObject_ContainsKey") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); - - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); - - JsonObject& obj = doc.as(); - REQUIRE(true == obj.containsKey(vla)); - } - - SECTION("JsonObject_Remove") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); - - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":\"world\"}"); - JsonObject& obj = doc.as(); - obj.remove(vla); - - REQUIRE(0 == obj.size()); - } - - SECTION("JsonObject_Is") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); - - DynamicJsonDocument doc; - deserializeJson(doc, "{\"hello\":42}"); - JsonObject& obj = doc.as(); - - REQUIRE(true == obj.is(vla)); - } - - SECTION("JsonObject_CreateNestedArray") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); - - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.createNestedArray(vla); - } - - SECTION("JsonObject_CreateNestedObject") { - int i = 16; - char vla[i]; - strcpy(vla, "hello"); - - DynamicJsonDocument doc; - JsonObject& obj = doc.to(); - obj.createNestedObject(vla); - } - - SECTION("JsonArray_Add") { - int i = 16; - char vla[i]; - strcpy(vla, "world"); - - DynamicJsonDocument doc; - JsonArray& arr = doc.to(); - arr.add(vla); - - REQUIRE(std::string("world") == arr[0]); - } - - SECTION("JsonArray_Set") { - int i = 16; - char vla[i]; - strcpy(vla, "world"); - - DynamicJsonDocument doc; - JsonArray& arr = doc.to(); - arr.add("hello"); - arr.set(0, vla); - - REQUIRE(std::string("world") == arr[0]); - } - - SECTION("JsonArraySubscript_Set") { - int i = 16; - char vla[i]; - strcpy(vla, "world"); - - DynamicJsonDocument doc; - JsonArray& arr = doc.to(); - arr.add("hello"); - arr[0].set(vla); - - REQUIRE(std::string("world") == arr[0]); - } - - SECTION("JsonArraySubscript_Assign") { - int i = 16; - char vla[i]; - strcpy(vla, "world"); - - DynamicJsonDocument doc; - JsonArray& arr = doc.to(); - arr.add("hello"); - arr[0] = vla; - - REQUIRE(std::string("world") == arr[0]); + REQUIRE(std::string("world") == arr[0]); + } } } #endif diff --git a/test/MsgPack/CMakeLists.txt b/test/MsgPack/CMakeLists.txt index b816549e..4565892b 100644 --- a/test/MsgPack/CMakeLists.txt +++ b/test/MsgPack/CMakeLists.txt @@ -3,13 +3,17 @@ # MIT License add_executable(MsgPackTests - deserializationErrors.cpp + MsgPackError.cpp deserializeArray.cpp deserializeObject.cpp - deserializeVariant.cpp deserializeStaticVariant.cpp + deserializeVariant.cpp doubleToFloat.cpp - MsgPackError.cpp + incompleteInput.cpp + nestingLimit.cpp + notSupported.cpp + std_string.cpp + std_istream.cpp ) target_link_libraries(MsgPackTests catch) diff --git a/test/MsgPack/MsgPackError.cpp b/test/MsgPack/MsgPackError.cpp index 3f070260..eba41434 100644 --- a/test/MsgPack/MsgPackError.cpp +++ b/test/MsgPack/MsgPackError.cpp @@ -25,6 +25,7 @@ TEST_CASE("MsgPackError") { TEST_STRINGIFICATION(NotSupported); TEST_STRINGIFICATION(NoMemory); TEST_STRINGIFICATION(TooDeep); + TEST_STRINGIFICATION(IncompleteInput); } SECTION("as boolean") { @@ -32,6 +33,7 @@ TEST_CASE("MsgPackError") { TEST_BOOLIFICATION(NotSupported, true); TEST_BOOLIFICATION(NoMemory, true); TEST_BOOLIFICATION(TooDeep, true); + TEST_BOOLIFICATION(IncompleteInput, true); } SECTION("ostream") { diff --git a/test/MsgPack/deserializationErrors.cpp b/test/MsgPack/deserializationErrors.cpp deleted file mode 100644 index 1ece18bc..00000000 --- a/test/MsgPack/deserializationErrors.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// ArduinoJson - arduinojson.org -// Copyright Benoit Blanchon 2014-2018 -// MIT License - -#include -#include - -static void check(const char* input, MsgPackError expected, - uint8_t nestingLimit = 10) { - DynamicJsonDocument doc; - doc.nestingLimit = nestingLimit; - - MsgPackError error = deserializeMsgPack(doc, input); - - REQUIRE(error == expected); -} - -TEST_CASE("Errors returned by deserializeMsgPack()") { - SECTION("unsupported") { - check("\xc4", MsgPackError::NotSupported); // bin 8 - check("\xc5", MsgPackError::NotSupported); // bin 16 - check("\xc6", MsgPackError::NotSupported); // bin 32 - check("\xc7", MsgPackError::NotSupported); // ext 8 - check("\xc8", MsgPackError::NotSupported); // ext 16 - check("\xc9", MsgPackError::NotSupported); // ext 32 - check("\xd4", MsgPackError::NotSupported); // fixext 1 - check("\xd5", MsgPackError::NotSupported); // fixext 2 - check("\xd6", MsgPackError::NotSupported); // fixext 4 - check("\xd7", MsgPackError::NotSupported); // fixext 8 - check("\xd8", MsgPackError::NotSupported); // fixext 16 - } - - SECTION("unsupported in array") { - check("\x91\xc4", MsgPackError::NotSupported); - } - - SECTION("unsupported in map") { - check("\x81\xc4\x00\xA1H", MsgPackError::NotSupported); - check("\x81\xA1H\xc4\x00", MsgPackError::NotSupported); - } - - SECTION("integer as key") { - check("\x81\x01\xA1H", MsgPackError::NotSupported); - } - - SECTION("object too deep") { - check("\x80", MsgPackError::TooDeep, 0); // {} - check("\x80", MsgPackError::Ok, 1); // {} - check("\x81\xA1H\x80", MsgPackError::TooDeep, 1); // {H:{}} - check("\x81\xA1H\x80", MsgPackError::Ok, 2); // {H:{}} - } - - SECTION("array too deep") { - check("\x90", MsgPackError::TooDeep, 0); // [] - check("\x90", MsgPackError::Ok, 1); // [] - check("\x91\x90", MsgPackError::TooDeep, 1); // [[]] - check("\x91\x90", MsgPackError::Ok, 2); // [[]] - } -} diff --git a/test/MsgPack/incompleteInput.cpp b/test/MsgPack/incompleteInput.cpp new file mode 100644 index 00000000..8d1715c8 --- /dev/null +++ b/test/MsgPack/incompleteInput.cpp @@ -0,0 +1,106 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +MsgPackError deserialize(const char* input, size_t len) { + DynamicJsonDocument doc; + + return deserializeMsgPack(doc, input, len); +} + +void checkAllSizes(const char* input, size_t len) { + REQUIRE(deserialize(input, len) == MsgPackError::Ok); + + while (--len) { + REQUIRE(deserialize(input, len) == MsgPackError::IncompleteInput); + } +} + +TEST_CASE("deserializeMsgPack() returns IncompleteInput") { + SECTION("empty input") { + checkAllSizes("\x00", 1); + } + + SECTION("fixarray") { + checkAllSizes("\x91\x01", 2); + } + + SECTION("array 16") { + checkAllSizes("\xDC\x00\x01\x01", 4); + } + + SECTION("array 32") { + checkAllSizes("\xDD\x00\x00\x00\x01\x01", 6); + } + + SECTION("fixmap") { + checkAllSizes("\x81\xA3one\x01", 6); + } + + SECTION("map 16") { + checkAllSizes("\xDE\x00\x01\xA3one\x01", 8); + } + + SECTION("map 32") { + checkAllSizes("\xDF\x00\x00\x00\x01\xA3one\x01", 10); + } + + SECTION("uint 8") { + checkAllSizes("\xcc\x01", 2); + } + + SECTION("uint 16") { + checkAllSizes("\xcd\x00\x01", 3); + } + + SECTION("uint 32") { + checkAllSizes("\xCE\x00\x00\x00\x01", 5); + } + + SECTION("uint 64") { + checkAllSizes("\xCF\x00\x00\x00\x00\x00\x00\x00\x00", 9); + } + + SECTION("int 8") { + checkAllSizes("\xD0\x01", 2); + } + + SECTION("int 16") { + checkAllSizes("\xD1\x00\x01", 3); + } + + SECTION("int 32") { + checkAllSizes("\xD2\x00\x00\x00\x01", 5); + } + + SECTION("int 64") { + checkAllSizes("\xD3\x00\x00\x00\x00\x00\x00\x00\x00", 9); + } + + SECTION("float 32") { + checkAllSizes("\xCA\x40\x48\xF5\xC3", 5); + } + + SECTION("float 64") { + checkAllSizes("\xCB\x40\x09\x21\xCA\xC0\x83\x12\x6F", 9); + } + + SECTION("fixstr") { + checkAllSizes("\xABhello world", 12); + } + + SECTION("str 8") { + checkAllSizes("\xd9\x05hello", 7); + } + + SECTION("str 16") { + checkAllSizes("\xda\x00\x05hello", 8); + } + + SECTION("str 32") { + checkAllSizes("\xdb\x00\x00\x00\x05hello", 10); + } +} diff --git a/test/MsgPack/nestingLimit.cpp b/test/MsgPack/nestingLimit.cpp new file mode 100644 index 00000000..a56acc2f --- /dev/null +++ b/test/MsgPack/nestingLimit.cpp @@ -0,0 +1,31 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +static void check(const char* input, MsgPackError expected, uint8_t limit) { + DynamicJsonDocument doc; + doc.nestingLimit = limit; + + MsgPackError error = deserializeMsgPack(doc, input); + + REQUIRE(error == expected); +} + +TEST_CASE("Errors returned by deserializeMsgPack()") { + SECTION("object too deep") { + check("\x80", MsgPackError::TooDeep, 0); // {} + check("\x80", MsgPackError::Ok, 1); // {} + check("\x81\xA1H\x80", MsgPackError::TooDeep, 1); // {H:{}} + check("\x81\xA1H\x80", MsgPackError::Ok, 2); // {H:{}} + } + + SECTION("array too deep") { + check("\x90", MsgPackError::TooDeep, 0); // [] + check("\x90", MsgPackError::Ok, 1); // [] + check("\x91\x90", MsgPackError::TooDeep, 1); // [[]] + check("\x91\x90", MsgPackError::Ok, 2); // [[]] + } +} diff --git a/test/MsgPack/notSupported.cpp b/test/MsgPack/notSupported.cpp new file mode 100644 index 00000000..e6ab6300 --- /dev/null +++ b/test/MsgPack/notSupported.cpp @@ -0,0 +1,73 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +static void checkNotSupported(const char* input) { + DynamicJsonDocument doc; + + MsgPackError error = deserializeMsgPack(doc, input); + + REQUIRE(error == MsgPackError::NotSupported); +} + +TEST_CASE("deserializeMsgPack() return NotSupported") { + SECTION("bin 8") { + checkNotSupported("\xc4"); + } + + SECTION("bin 16") { + checkNotSupported("\xc5"); + } + + SECTION("bin 32") { + checkNotSupported("\xc6"); + } + + SECTION("ext 8") { + checkNotSupported("\xc7"); + } + + SECTION("ext 16") { + checkNotSupported("\xc8"); + } + + SECTION("ext 32") { + checkNotSupported("\xc9"); + } + + SECTION("fixext 1") { + checkNotSupported("\xd4"); + } + + SECTION("fixext 2") { + checkNotSupported("\xd5"); + } + + SECTION("fixext 4") { + checkNotSupported("\xd6"); + } + + SECTION("fixext 8") { + checkNotSupported("\xd7"); + } + + SECTION("fixext 16") { + checkNotSupported("\xd8"); + } + + SECTION("unsupported in array") { + checkNotSupported("\x91\xc4"); + } + + SECTION("unsupported in map") { + checkNotSupported("\x81\xc4\x00\xA1H"); + checkNotSupported("\x81\xA1H\xc4\x00"); + } + + SECTION("integer as key") { + checkNotSupported("\x81\x01\xA1H"); + } +} diff --git a/test/MsgPack/std_istream.cpp b/test/MsgPack/std_istream.cpp new file mode 100644 index 00000000..d04dc935 --- /dev/null +++ b/test/MsgPack/std_istream.cpp @@ -0,0 +1,29 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("deserializeMsgPack(std::istream&)") { + DynamicJsonDocument doc; + + SECTION("should accept a zero in input") { + std::istringstream input(std::string("\x92\x00\x02", 3)); + + MsgPackError err = deserializeMsgPack(doc, input); + + REQUIRE(err == MsgPackError::Ok); + JsonArray& arr = doc.as(); + REQUIRE(arr[0] == 0); + REQUIRE(arr[1] == 2); + } + + SECTION("should detect incomplete input") { + std::istringstream input("\x92\x00\x02"); + + MsgPackError err = deserializeMsgPack(doc, input); + + REQUIRE(err == MsgPackError::IncompleteInput); + } +} diff --git a/test/MsgPack/std_string.cpp b/test/MsgPack/std_string.cpp new file mode 100644 index 00000000..bc374719 --- /dev/null +++ b/test/MsgPack/std_string.cpp @@ -0,0 +1,44 @@ +// ArduinoJson - arduinojson.org +// Copyright Benoit Blanchon 2014-2018 +// MIT License + +#include +#include + +TEST_CASE("deserializeMsgPack(const std::string&)") { + DynamicJsonDocument doc; + + SECTION("should accept const string") { + const std::string input("\x92\x01\x02"); + + MsgPackError err = deserializeMsgPack(doc, input); + + REQUIRE(err == MsgPackError::Ok); + } + + SECTION("should accept temporary string") { + MsgPackError err = deserializeMsgPack(doc, std::string("\x92\x01\x02")); + + REQUIRE(err == MsgPackError::Ok); + } + + SECTION("should duplicate content") { + std::string input("\x91\xA5hello"); + + MsgPackError err = deserializeMsgPack(doc, input); + input[2] = 'X'; // alter the string tomake sure we made a copy + + JsonArray& array = doc.as(); + REQUIRE(err == MsgPackError::Ok); + REQUIRE(std::string("hello") == array[0]); + } + + SECTION("should accept a zero in input") { + MsgPackError err = deserializeMsgPack(doc, std::string("\x92\x00\x02", 3)); + + REQUIRE(err == MsgPackError::Ok); + JsonArray& arr = doc.as(); + REQUIRE(arr[0] == 0); + REQUIRE(arr[1] == 2); + } +}