mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 16:14:11 +01:00 
			
		
		
		
	Removed implicit conversion in comparison operators (issue #998)
This commit is contained in:
		| @@ -9,6 +9,8 @@ HEAD | ||||
| * Made `deserializeJson()` more picky about trailing characters (issue #980) | ||||
| * Added `ARDUINOJSON_ENABLE_NAN` (default=0) to enable NaN in JSON (issue #973) | ||||
| * Added `ARDUINOJSON_ENABLE_INFINITY` (default=0) to enable Infinity in JSON | ||||
| * Removed implicit conversion in comparison operators (issue #998) | ||||
| * Added lexicographical comparison for `JsonVariant` | ||||
|  | ||||
| > ### BREAKING CHANGES | ||||
| >  | ||||
|   | ||||
| @@ -7,154 +7,216 @@ | ||||
| #include "../Variant/VariantRef.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| template <typename T, typename Enable = void> | ||||
| struct Comparer; | ||||
|  | ||||
| template <typename T> | ||||
| struct is_simple_value { | ||||
|   static const bool value = is_integral<T>::value || | ||||
|                             is_floating_point<T>::value || | ||||
|                             is_same<T, bool>::value; | ||||
| struct Comparer<T, typename enable_if<IsString<T>::value>::type> { | ||||
|   T rhs; | ||||
|   int result; | ||||
|  | ||||
|   explicit Comparer(T value) : rhs(value), result(1) {} | ||||
|  | ||||
|   void visitArray(const CollectionData &) {} | ||||
|   void visitObject(const CollectionData &) {} | ||||
|   void visitFloat(Float) {} | ||||
|   void visitString(const char *lhs) { | ||||
|     result = -adaptString(rhs).compare(lhs); | ||||
|   } | ||||
|   void visitRawJson(const char *, size_t) {} | ||||
|   void visitNegativeInteger(UInt) {} | ||||
|   void visitPositiveInteger(UInt) {} | ||||
|   void visitBoolean(bool) {} | ||||
|   void visitNull() { | ||||
|     result = adaptString(rhs).compare(NULL); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename T> | ||||
| typename enable_if<is_signed<T>::value, int>::type sign(const T &value) { | ||||
|   return value < 0 ? -1 : value > 0 ? 1 : 0; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| typename enable_if<is_unsigned<T>::value, int>::type sign(const T &value) { | ||||
|   return value > 0 ? 1 : 0; | ||||
| } | ||||
|  | ||||
| template <typename T> | ||||
| struct Comparer<T, typename enable_if<is_integral<T>::value || | ||||
|                                       is_floating_point<T>::value>::type> { | ||||
|   T rhs; | ||||
|   int result; | ||||
|  | ||||
|   explicit Comparer(T value) : rhs(value), result(1) {} | ||||
|  | ||||
|   void visitArray(const CollectionData &) {} | ||||
|   void visitObject(const CollectionData &) {} | ||||
|   void visitFloat(Float lhs) { | ||||
|     result = sign(lhs - static_cast<Float>(rhs)); | ||||
|   } | ||||
|   void visitString(const char *) {} | ||||
|   void visitRawJson(const char *, size_t) {} | ||||
|   void visitNegativeInteger(UInt lhs) { | ||||
|     result = -sign(static_cast<T>(lhs) + rhs); | ||||
|   } | ||||
|   void visitPositiveInteger(UInt lhs) { | ||||
|     result = static_cast<T>(lhs) < rhs ? -1 : static_cast<T>(lhs) > rhs ? 1 : 0; | ||||
|   } | ||||
|   void visitBoolean(bool) {} | ||||
|   void visitNull() {} | ||||
| }; | ||||
|  | ||||
| template <> | ||||
| struct Comparer<bool, void> { | ||||
|   bool rhs; | ||||
|   int result; | ||||
|  | ||||
|   explicit Comparer(bool value) : rhs(value), result(1) {} | ||||
|  | ||||
|   void visitArray(const CollectionData &) {} | ||||
|   void visitObject(const CollectionData &) {} | ||||
|   void visitFloat(Float) {} | ||||
|   void visitString(const char *) {} | ||||
|   void visitRawJson(const char *, size_t) {} | ||||
|   void visitNegativeInteger(UInt) {} | ||||
|   void visitPositiveInteger(UInt) {} | ||||
|   void visitBoolean(bool lhs) { | ||||
|     result = static_cast<int>(lhs - rhs); | ||||
|   } | ||||
|   void visitNull() {} | ||||
| }; | ||||
|  | ||||
| template <typename TVariant> | ||||
| class VariantComparisons { | ||||
|  private: | ||||
|   template <typename T> | ||||
|   static int compare(TVariant lhs, const T &rhs) { | ||||
|     Comparer<T> comparer(rhs); | ||||
|     lhs.accept(comparer); | ||||
|     return comparer.result; | ||||
|   } | ||||
|  | ||||
|  public: | ||||
|   // const char* == TVariant | ||||
|   // value == TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<IsString<T *>::value, bool>::type operator==( | ||||
|       T *lhs, TVariant rhs) { | ||||
|     return adaptString(lhs).equals(rhs.template as<const char *>()); | ||||
|   friend bool operator==(T *lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) == 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator==(const T &lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) == 0; | ||||
|   } | ||||
|  | ||||
|   // std::string == TVariant | ||||
|   // TVariant == value | ||||
|   template <typename T> | ||||
|   friend typename enable_if<IsString<T>::value, bool>::type operator==( | ||||
|       const T &lhs, TVariant rhs) { | ||||
|     return adaptString(lhs).equals(rhs.template as<const char *>()); | ||||
|   friend bool operator==(TVariant lhs, T *rhs) { | ||||
|     return compare(lhs, rhs) == 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator==(TVariant lhs, const T &rhs) { | ||||
|     return compare(lhs, rhs) == 0; | ||||
|   } | ||||
|  | ||||
|   // TVariant == const char* | ||||
|   // value != TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<IsString<T *>::value, bool>::type operator==( | ||||
|       TVariant lhs, T *rhs) { | ||||
|     return adaptString(rhs).equals(lhs.template as<const char *>()); | ||||
|   friend bool operator!=(T *lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) != 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator!=(const T &lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) != 0; | ||||
|   } | ||||
|  | ||||
|   // TVariant == std::string | ||||
|   // TVariant != value | ||||
|   template <typename T> | ||||
|   friend typename enable_if<IsString<T>::value, bool>::type operator==( | ||||
|       TVariant lhs, const T &rhs) { | ||||
|     return adaptString(rhs).equals(lhs.template as<const char *>()); | ||||
|   friend bool operator!=(TVariant lhs, T *rhs) { | ||||
|     return compare(lhs, rhs) != 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator!=(TVariant lhs, const T &rhs) { | ||||
|     return compare(lhs, rhs) != 0; | ||||
|   } | ||||
|  | ||||
|   // bool/int/float == TVariant | ||||
|   // value < TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator==( | ||||
|       const T &lhs, TVariant rhs) { | ||||
|     return lhs == rhs.template as<T>(); | ||||
|   friend bool operator<(T *lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) > 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator<(const T &lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) > 0; | ||||
|   } | ||||
|  | ||||
|   // TVariant == bool/int/float | ||||
|   // TVariant < value | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator==( | ||||
|       TVariant lhs, const T &rhs) { | ||||
|     return lhs.template as<T>() == rhs; | ||||
|   friend bool operator<(TVariant lhs, T *rhs) { | ||||
|     return compare(lhs, rhs) < 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator<(TVariant lhs, const T &rhs) { | ||||
|     return compare(lhs, rhs) < 0; | ||||
|   } | ||||
|  | ||||
|   // const char* != TVariant | ||||
|   // value <= TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<IsString<T *>::value, bool>::type operator!=( | ||||
|       T *lhs, TVariant rhs) { | ||||
|     return !adaptString(lhs).equals(rhs.template as<const char *>()); | ||||
|   friend bool operator<=(T *lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) >= 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator<=(const T &lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) >= 0; | ||||
|   } | ||||
|  | ||||
|   // std::string != TVariant | ||||
|   // TVariant <= value | ||||
|   template <typename T> | ||||
|   friend typename enable_if<IsString<T>::value, bool>::type operator!=( | ||||
|       const T &lhs, TVariant rhs) { | ||||
|     return !adaptString(lhs).equals(rhs.template as<const char *>()); | ||||
|   friend bool operator<=(TVariant lhs, T *rhs) { | ||||
|     return compare(lhs, rhs) <= 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator<=(TVariant lhs, const T &rhs) { | ||||
|     return compare(lhs, rhs) <= 0; | ||||
|   } | ||||
|  | ||||
|   // TVariant != const char* | ||||
|   // value > TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<IsString<T *>::value, bool>::type operator!=( | ||||
|       TVariant lhs, T *rhs) { | ||||
|     return !adaptString(rhs).equals(lhs.template as<const char *>()); | ||||
|   friend bool operator>(T *lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) < 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator>(const T &lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) < 0; | ||||
|   } | ||||
|  | ||||
|   // TVariant != std::string | ||||
|   // TVariant > value | ||||
|   template <typename T> | ||||
|   friend typename enable_if<IsString<T>::value, bool>::type operator!=( | ||||
|       TVariant lhs, const T &rhs) { | ||||
|     return !adaptString(rhs).equals(lhs.template as<const char *>()); | ||||
|   friend bool operator>(TVariant lhs, T *rhs) { | ||||
|     return compare(lhs, rhs) > 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator>(TVariant lhs, const T &rhs) { | ||||
|     return compare(lhs, rhs) > 0; | ||||
|   } | ||||
|  | ||||
|   // bool/int/float != TVariant | ||||
|   // value >= TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator!=( | ||||
|       const T &lhs, TVariant rhs) { | ||||
|     return lhs != rhs.template as<T>(); | ||||
|   friend bool operator>=(T *lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) <= 0; | ||||
|   } | ||||
|   template <typename T> | ||||
|   friend bool operator>=(const T &lhs, TVariant rhs) { | ||||
|     return compare(rhs, lhs) <= 0; | ||||
|   } | ||||
|  | ||||
|   // TVariant != bool/int/float | ||||
|   // TVariant >= value | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator!=( | ||||
|       TVariant lhs, const T &rhs) { | ||||
|     return lhs.template as<T>() != rhs; | ||||
|   friend bool operator>=(TVariant lhs, T *rhs) { | ||||
|     return compare(lhs, rhs) >= 0; | ||||
|   } | ||||
|  | ||||
|   // bool/int/float < TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator<( | ||||
|       const T &lhs, TVariant rhs) { | ||||
|     return lhs < rhs.template as<T>(); | ||||
|   } | ||||
|  | ||||
|   // TVariant < bool/int/float | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator<( | ||||
|       TVariant lhs, const T &rhs) { | ||||
|     return lhs.template as<T>() < rhs; | ||||
|   } | ||||
|  | ||||
|   // bool/int/float <= TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator<=( | ||||
|       const T &lhs, TVariant rhs) { | ||||
|     return lhs <= rhs.template as<T>(); | ||||
|   } | ||||
|  | ||||
|   // TVariant <= bool/int/float | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator<=( | ||||
|       TVariant lhs, const T &rhs) { | ||||
|     return lhs.template as<T>() <= rhs; | ||||
|   } | ||||
|  | ||||
|   // bool/int/float > TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator>( | ||||
|       const T &lhs, TVariant rhs) { | ||||
|     return lhs > rhs.template as<T>(); | ||||
|   } | ||||
|  | ||||
|   // TVariant > bool/int/float | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator>( | ||||
|       TVariant lhs, const T &rhs) { | ||||
|     return lhs.template as<T>() > rhs; | ||||
|   } | ||||
|  | ||||
|   // bool/int/float >= TVariant | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator>=( | ||||
|       const T &lhs, TVariant rhs) { | ||||
|     return lhs >= rhs.template as<T>(); | ||||
|   } | ||||
|  | ||||
|   // TVariant >= bool/int/float | ||||
|   template <typename T> | ||||
|   friend typename enable_if<is_simple_value<T>::value, bool>::type operator>=( | ||||
|       TVariant lhs, const T &rhs) { | ||||
|     return lhs.template as<T>() >= rhs; | ||||
|   friend bool operator>=(TVariant lhs, const T &rhs) { | ||||
|     return compare(lhs, rhs) >= 0; | ||||
|   } | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										22
									
								
								src/ArduinoJson/Polyfills/safe_strcmp.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/ArduinoJson/Polyfills/safe_strcmp.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| // ArduinoJson - arduinojson.org | ||||
| // Copyright Benoit Blanchon 2014-2019 | ||||
| // MIT License | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| inline int8_t safe_strcmp(const char* a, const char* b) { | ||||
|   if (a == b) return 0; | ||||
|   if (!a) return -1; | ||||
|   if (!b) return 1; | ||||
|   return static_cast<int8_t>(strcmp(a, b)); | ||||
| } | ||||
|  | ||||
| inline int8_t safe_strncmp(const char* a, const char* b, size_t n) { | ||||
|   if (a == b) return 0; | ||||
|   if (!a) return -1; | ||||
|   if (!b) return 1; | ||||
|   return static_cast<int8_t>(strncmp(a, b, n)); | ||||
| } | ||||
| }  // namespace ARDUINOJSON_NAMESPACE | ||||
| @@ -5,6 +5,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <WString.h> | ||||
| #include "../Polyfills/safe_strcmp.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| @@ -25,11 +26,14 @@ class ArduinoStringAdapter { | ||||
|     return !_str->c_str(); | ||||
|   } | ||||
|  | ||||
|   bool equals(const char* expected) const { | ||||
|   int8_t compare(const char* other) const { | ||||
|     // Arduino's String::c_str() can return NULL | ||||
|     const char* actual = _str->c_str(); | ||||
|     if (!actual || !expected) return actual == expected; | ||||
|     return 0 == strcmp(actual, expected); | ||||
|     const char* me = _str->c_str(); | ||||
|     return safe_strcmp(me, other); | ||||
|   } | ||||
|  | ||||
|   bool equals(const char* expected) const { | ||||
|     return compare(expected) == 0; | ||||
|   } | ||||
|  | ||||
|   const char* data() const { | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| #include <stddef.h>  // size_t | ||||
| #include <string.h>  // strcmp | ||||
| #include "../Polyfills/safe_strcmp.hpp" | ||||
|  | ||||
| namespace ARDUINOJSON_NAMESPACE { | ||||
|  | ||||
| @@ -13,10 +14,12 @@ class ConstRamStringAdapter { | ||||
|  public: | ||||
|   ConstRamStringAdapter(const char* str = 0) : _str(str) {} | ||||
|  | ||||
|   int8_t compare(const char* other) const { | ||||
|     return safe_strcmp(_str, other); | ||||
|   } | ||||
|  | ||||
|   bool equals(const char* expected) const { | ||||
|     const char* actual = _str; | ||||
|     if (!actual || !expected) return actual == expected; | ||||
|     return strcmp(actual, expected) == 0; | ||||
|     return compare(expected) == 0; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|   | ||||
| @@ -10,10 +10,15 @@ class FlashStringAdapter { | ||||
|  public: | ||||
|   FlashStringAdapter(const __FlashStringHelper* str) : _str(str) {} | ||||
|  | ||||
|   int8_t compare(const char* other) const { | ||||
|     if (!other && !_str) return 0; | ||||
|     if (!_str) return -1; | ||||
|     if (!other) return 1; | ||||
|     return -strcmp_P(other, reinterpret_cast<const char*>(_str)); | ||||
|   } | ||||
|  | ||||
|   bool equals(const char* expected) const { | ||||
|     const char* actual = reinterpret_cast<const char*>(_str); | ||||
|     if (!actual || !expected) return actual == expected; | ||||
|     return strcmp_P(expected, actual) == 0; | ||||
|     return compare(expected) == 0; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|   | ||||
| @@ -11,10 +11,15 @@ class SizedFlashStringAdapter { | ||||
|   SizedFlashStringAdapter(const __FlashStringHelper* str, size_t sz) | ||||
|       : _str(str), _size(sz) {} | ||||
|  | ||||
|   int8_t compare(const char* other) const { | ||||
|     if (!other && !_str) return 0; | ||||
|     if (!_str) return -1; | ||||
|     if (!other) return 1; | ||||
|     return -strncmp_P(other, reinterpret_cast<const char*>(_str), _size); | ||||
|   } | ||||
|  | ||||
|   bool equals(const char* expected) const { | ||||
|     const char* actual = reinterpret_cast<const char*>(_str); | ||||
|     if (!actual || !expected) return actual == expected; | ||||
|     return strncmp_P(expected, actual, _size) == 0; | ||||
|     return compare(expected) == 0; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|   | ||||
| @@ -12,10 +12,12 @@ class SizedRamStringAdapter { | ||||
|  public: | ||||
|   SizedRamStringAdapter(const char* str, size_t n) : _str(str), _size(n) {} | ||||
|  | ||||
|   int8_t compare(const char* other) const { | ||||
|     return safe_strncmp(_str, other, _size) == 0; | ||||
|   } | ||||
|  | ||||
|   bool equals(const char* expected) const { | ||||
|     const char* actual = reinterpret_cast<const char*>(_str); | ||||
|     if (!actual || !expected) return actual == expected; | ||||
|     return strcmp(actual, expected) == 0; | ||||
|     return compare(expected) == 0; | ||||
|   } | ||||
|  | ||||
|   bool isNull() const { | ||||
|   | ||||
| @@ -23,6 +23,11 @@ class StlStringAdapter { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   int8_t compare(const char* other) const { | ||||
|     if (!other) return 1; | ||||
|     return static_cast<int8_t>(_str->compare(other)); | ||||
|   } | ||||
|  | ||||
|   bool equals(const char* expected) const { | ||||
|     if (!expected) return false; | ||||
|     return *_str == expected; | ||||
|   | ||||
| @@ -161,7 +161,7 @@ TEST_CASE("JsonObject::operator[]") { | ||||
|     obj[null] = 666; | ||||
|  | ||||
|     REQUIRE(obj.size() == 1); | ||||
|     REQUIRE(obj[null] == 0); | ||||
|     REQUIRE(obj[null] == null); | ||||
|   } | ||||
|  | ||||
|   SECTION("obj[key].to<JsonArray>()") { | ||||
|   | ||||
| @@ -337,23 +337,117 @@ TEST_CASE("JsonVariant comparisons") { | ||||
|     REQUIRE(variant1 != variant3); | ||||
|     REQUIRE_FALSE(variant1 == variant3); | ||||
|   } | ||||
|  | ||||
|   // SECTION("VariantsOfDifferentTypes") { | ||||
|   //   DynamicJsonDocument doc1(4096); | ||||
|   //   JsonObject obj = doc1.to<JsonObject>(); | ||||
|  | ||||
|   //   DynamicJsonDocument doc2(4096); | ||||
|   //   JsonArray arr = doc2.to<JsonArray>(); | ||||
|   //   JsonVariant variants[] = { | ||||
|   //       true, 42, 666.667, "hello", arr, obj, | ||||
|   //   }; | ||||
|   //   size_t n = sizeof(variants) / sizeof(variants[0]); | ||||
|  | ||||
|   //   for (size_t i = 0; i < n; i++) { | ||||
|   //     for (size_t j = i + 1; j < n; j++) { | ||||
|   //       REQUIRE(variants[i] != variants[j]); | ||||
|   //       REQUIRE_FALSE(variants[i] == variants[j]); | ||||
|   //     } | ||||
|   //   } | ||||
|   // } | ||||
| } | ||||
|  | ||||
| class VariantComparisionFixture { | ||||
|  private: | ||||
|   StaticJsonDocument<256> doc; | ||||
|   JsonVariant variant; | ||||
|  | ||||
|  public: | ||||
|   VariantComparisionFixture() : variant(doc.to<JsonVariant>()) {} | ||||
|  | ||||
|  protected: | ||||
|   template <typename T> | ||||
|   void setValue(const T& value) { | ||||
|     variant.set(value); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   void assertEqualsTo(const T& value) { | ||||
|     REQUIRE(variant == value); | ||||
|     REQUIRE(value == variant); | ||||
|  | ||||
|     REQUIRE_FALSE(variant != value); | ||||
|     REQUIRE_FALSE(value != variant); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   void assertDiffersFrom(const T& value) { | ||||
|     REQUIRE(variant != value); | ||||
|     REQUIRE(value != variant); | ||||
|  | ||||
|     REQUIRE_FALSE(variant == value); | ||||
|     REQUIRE_FALSE(value == variant); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   void assertGreaterThan(const T& value) { | ||||
|     REQUIRE((variant > value)); | ||||
|     REQUIRE((variant >= value)); | ||||
|     REQUIRE(value < variant); | ||||
|     REQUIRE(value <= variant); | ||||
|  | ||||
|     REQUIRE_FALSE((variant < value)); | ||||
|     REQUIRE_FALSE((variant <= value)); | ||||
|     REQUIRE_FALSE(value > variant); | ||||
|     REQUIRE_FALSE(value >= variant); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   void assertLowerThan(const T& value) { | ||||
|     REQUIRE(variant < value); | ||||
|     REQUIRE(variant <= value); | ||||
|     REQUIRE(value > variant); | ||||
|     REQUIRE(value >= variant); | ||||
|  | ||||
|     REQUIRE_FALSE(variant > value); | ||||
|     REQUIRE_FALSE(variant >= value); | ||||
|     REQUIRE_FALSE(value < variant); | ||||
|     REQUIRE_FALSE(value <= variant); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| TEST_CASE_METHOD(VariantComparisionFixture, | ||||
|                  "Compare variant with another type") { | ||||
|   SECTION("null") { | ||||
|     assertDiffersFrom(3); | ||||
|     assertDiffersFrom("world"); | ||||
|   } | ||||
|  | ||||
|   SECTION("string") { | ||||
|     setValue("hello"); | ||||
|     assertEqualsTo("hello"); | ||||
|     assertDiffersFrom(3); | ||||
|     assertDiffersFrom("world"); | ||||
|     assertGreaterThan("helln"); | ||||
|     assertLowerThan("hellp"); | ||||
|   } | ||||
|  | ||||
|   SECTION("positive integer") { | ||||
|     setValue(42); | ||||
|     assertEqualsTo(42); | ||||
|     assertDiffersFrom(43); | ||||
|     assertGreaterThan(41); | ||||
|     assertLowerThan(43); | ||||
|     assertDiffersFrom("world"); | ||||
|   } | ||||
|  | ||||
|   SECTION("negative integer") { | ||||
|     setValue(-42); | ||||
|     assertEqualsTo(-42); | ||||
|     assertDiffersFrom(42); | ||||
|     assertGreaterThan(-43); | ||||
|     assertLowerThan(-41); | ||||
|     assertDiffersFrom("world"); | ||||
|   } | ||||
|  | ||||
|   SECTION("double") { | ||||
|     setValue(42.0); | ||||
|     assertEqualsTo(42.0); | ||||
|     assertDiffersFrom(42.1); | ||||
|     assertGreaterThan(41.0); | ||||
|     assertLowerThan(43.0); | ||||
|     assertDiffersFrom("42.0"); | ||||
|   } | ||||
|  | ||||
|   SECTION("true") { | ||||
|     setValue(true); | ||||
|     assertEqualsTo(true); | ||||
|     assertDiffersFrom(false); | ||||
|     assertDiffersFrom(1); | ||||
|     assertDiffersFrom("true"); | ||||
|     assertDiffersFrom(1.0); | ||||
|     assertGreaterThan(false); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -14,7 +14,7 @@ TEST_CASE("JsonVariant::createNestedObject()") { | ||||
|  | ||||
|   SECTION("promotes to array") { | ||||
|     JsonObject obj = variant.createNestedObject(); | ||||
|     obj["value"] = "42"; | ||||
|     obj["value"] = 42; | ||||
|  | ||||
|     REQUIRE(variant.is<JsonArray>() == true); | ||||
|     REQUIRE(variant[0]["value"] == 42); | ||||
| @@ -23,7 +23,7 @@ TEST_CASE("JsonVariant::createNestedObject()") { | ||||
|  | ||||
|   SECTION("works on MemberProxy") { | ||||
|     JsonObject obj = variant["items"].createNestedObject(); | ||||
|     obj["value"] = "42"; | ||||
|     obj["value"] = 42; | ||||
|  | ||||
|     REQUIRE(variant["items"][0]["value"] == 42); | ||||
|   } | ||||
| @@ -42,7 +42,7 @@ TEST_CASE("JsonVariant::createNestedArray()") { | ||||
|  | ||||
|   SECTION("works on MemberProxy") { | ||||
|     JsonArray arr = variant["items"].createNestedArray(); | ||||
|     arr.add("42"); | ||||
|     arr.add(42); | ||||
|  | ||||
|     REQUIRE(variant["items"][0][0] == 42); | ||||
|   } | ||||
| @@ -54,7 +54,7 @@ TEST_CASE("JsonVariant::createNestedObject(key)") { | ||||
|  | ||||
|   SECTION("promotes to object") { | ||||
|     JsonObject obj = variant.createNestedObject("weather"); | ||||
|     obj["temp"] = "42"; | ||||
|     obj["temp"] = 42; | ||||
|  | ||||
|     REQUIRE(variant.is<JsonObject>() == true); | ||||
|     REQUIRE(variant["weather"]["temp"] == 42); | ||||
| @@ -62,7 +62,7 @@ TEST_CASE("JsonVariant::createNestedObject(key)") { | ||||
|  | ||||
|   SECTION("works on MemberProxy") { | ||||
|     JsonObject obj = variant["status"].createNestedObject("weather"); | ||||
|     obj["temp"] = "42"; | ||||
|     obj["temp"] = 42; | ||||
|  | ||||
|     REQUIRE(variant["status"]["weather"]["temp"] == 42); | ||||
|   } | ||||
| @@ -81,7 +81,7 @@ TEST_CASE("JsonVariant::createNestedArray(key)") { | ||||
|  | ||||
|   SECTION("works on MemberProxy") { | ||||
|     JsonArray arr = variant["weather"].createNestedArray("temp"); | ||||
|     arr.add("42"); | ||||
|     arr.add(42); | ||||
|  | ||||
|     REQUIRE(variant["weather"]["temp"][0] == 42); | ||||
|   } | ||||
|   | ||||
| @@ -6,6 +6,7 @@ add_executable(MiscTests | ||||
| 	conflicts.cpp | ||||
| 	FloatParts.cpp | ||||
| 	StreamReader.cpp | ||||
| 	StringAdapters.cpp | ||||
| 	StringWriter.cpp | ||||
| 	TypeTraits.cpp | ||||
| 	unsigned_char.cpp | ||||
|   | ||||
							
								
								
									
										45
									
								
								test/Misc/StringAdapters.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								test/Misc/StringAdapters.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| // ArduinoJson - arduinojson.org | ||||
| // Copyright Benoit Blanchon 2014-2019 | ||||
| // MIT License | ||||
|  | ||||
| #include <ArduinoJson.h> | ||||
| #include <catch.hpp> | ||||
|  | ||||
| using namespace ARDUINOJSON_NAMESPACE; | ||||
|  | ||||
| TEST_CASE("ConstRamStringAdapter") { | ||||
|   SECTION("null") { | ||||
|     ConstRamStringAdapter adapter(NULL); | ||||
|  | ||||
|     REQUIRE(adapter.compare("bravo") < 0); | ||||
|     REQUIRE(adapter.compare(NULL) == 0); | ||||
|  | ||||
|     REQUIRE(adapter.equals(NULL)); | ||||
|     REQUIRE_FALSE(adapter.equals("charlie")); | ||||
|   } | ||||
|  | ||||
|   SECTION("non-null") { | ||||
|     ConstRamStringAdapter adapter("bravo"); | ||||
|  | ||||
|     REQUIRE(adapter.compare(NULL) > 0); | ||||
|     REQUIRE(adapter.compare("alpha") > 0); | ||||
|     REQUIRE(adapter.compare("bravo") == 0); | ||||
|     REQUIRE(adapter.compare("charlie") < 0); | ||||
|  | ||||
|     REQUIRE(adapter.equals("bravo")); | ||||
|     REQUIRE_FALSE(adapter.equals("charlie")); | ||||
|   } | ||||
| } | ||||
|  | ||||
| TEST_CASE("StlString") { | ||||
|   std::string str("bravo"); | ||||
|   StlStringAdapter adapter(str); | ||||
|  | ||||
|   REQUIRE(adapter.compare(NULL) > 0); | ||||
|   REQUIRE(adapter.compare("alpha") > 0); | ||||
|   REQUIRE(adapter.compare("bravo") == 0); | ||||
|   REQUIRE(adapter.compare("charlie") < 0); | ||||
|  | ||||
|   REQUIRE(adapter.equals("bravo")); | ||||
|   REQUIRE_FALSE(adapter.equals("charlie")); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user