diff options
Diffstat (limited to 'src/VBox/Runtime/testcase/tstRTCRest-1.cpp')
-rw-r--r-- | src/VBox/Runtime/testcase/tstRTCRest-1.cpp | 2295 |
1 files changed, 2295 insertions, 0 deletions
diff --git a/src/VBox/Runtime/testcase/tstRTCRest-1.cpp b/src/VBox/Runtime/testcase/tstRTCRest-1.cpp new file mode 100644 index 00000000..e96c1e10 --- /dev/null +++ b/src/VBox/Runtime/testcase/tstRTCRest-1.cpp @@ -0,0 +1,2295 @@ +/* $Id: tstRTCRest-1.cpp $ */ +/** @file + * IPRT Testcase - REST C++ classes. + */ + +/* + * Copyright (C) 2018-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/cpp/restbase.h> +#include <iprt/cpp/restarray.h> +#include <iprt/cpp/reststringmap.h> +#include <iprt/cpp/restclient.h> +#include <iprt/cpp/restoutput.h> + +#include <iprt/ctype.h> +#include <iprt/err.h> +#include <iprt/message.h> +#include <iprt/string.h> +#include <iprt/test.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static RTTEST g_hTest; + + +static char *toJson(RTCRestObjectBase const *pObj) +{ + RTCString str; + RTCRestOutputToString Dst(&str, false); + pObj->serializeAsJson(Dst); + + static char s_szReturnBuffer[4096]; + RTStrCopy(s_szReturnBuffer, sizeof(s_szReturnBuffer), str.c_str()); + return s_szReturnBuffer; +} + + +static int deserializeFromJson(RTCRestObjectBase *pObj, const char *pszJson, PRTERRINFOSTATIC pErrInfo, const char *pszName) +{ + RTJSONVAL hValue; + RTTESTI_CHECK_RC_OK_RET(RTJsonParseFromString(&hValue, pszJson, pErrInfo ? RTErrInfoInitStatic(pErrInfo) : NULL), rcCheck); + RTCRestJsonPrimaryCursor Cursor(hValue, pszName, pErrInfo ? RTErrInfoInitStatic(pErrInfo) : NULL); + return pObj->deserializeFromJson(Cursor.m_Cursor); +} + + +static int fromString(RTCRestObjectBase *pObj, const char *pszString, PRTERRINFOSTATIC pErrInfo, const char *pszName) +{ + RTCString strValue(pszString); + return pObj->fromString(strValue, pszName, pErrInfo ? RTErrInfoInitStatic(pErrInfo) : NULL); +} + + +static void testBool(void) +{ + RTTestSub(g_hTest, "RTCRestBool"); + + { + RTCRestBool obj1; + RTTESTI_CHECK(obj1.m_fValue == false); + RTTESTI_CHECK(obj1.isNull() == false); + RTTESTI_CHECK(strcmp(obj1.typeName(), "bool") == 0); + RTTESTI_CHECK(obj1.typeClass() == RTCRestObjectBase::kTypeClass_Bool); + } + + { + RTCRestBool obj2(true); + RTTESTI_CHECK(obj2.m_fValue == true); + RTTESTI_CHECK(obj2.isNull() == false); + } + + { + RTCRestBool obj2(false); + RTTESTI_CHECK(obj2.m_fValue == false); + RTTESTI_CHECK(obj2.isNull() == false); + } + + { + /* Value assignments: */ + RTCRestBool obj3; + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + obj3.assignValue(true); + RTTESTI_CHECK(obj3.m_fValue == true); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + obj3.assignValue(false); + RTTESTI_CHECK(obj3.m_fValue == false); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3.assignValue(true); + RTTESTI_CHECK(obj3.m_fValue == true); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.resetToDefault(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_fValue == false); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3.assignValue(true); + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.resetToDefault(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_fValue == false); + RTTESTI_CHECK(obj3.isNull() == false); + + /* Copy assignments: */ + RTCRestBool obj3True(true); + RTTESTI_CHECK(obj3True.m_fValue == true); + RTTESTI_CHECK(obj3True.isNull() == false); + RTCRestBool obj3False(false); + RTTESTI_CHECK(obj3False.m_fValue == false); + RTTESTI_CHECK(obj3False.isNull() == false); + RTCRestBool obj3Null; + obj3Null.setNull(); + RTTESTI_CHECK(obj3Null.m_fValue == false); + RTTESTI_CHECK(obj3Null.isNull() == true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignCopy(obj3True), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_fValue == true); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Null), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_fValue == false); + RTTESTI_CHECK(obj3.isNull() == true); + + RTTESTI_CHECK_RC(obj3.assignCopy(obj3False), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_fValue == false); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3 = obj3Null; + RTTESTI_CHECK(obj3.m_fValue == false); + RTTESTI_CHECK(obj3.isNull() == true); + + obj3 = obj3True; + RTTESTI_CHECK(obj3.m_fValue == true); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3 = obj3Null; + RTTESTI_CHECK(obj3.m_fValue == false); + RTTESTI_CHECK(obj3.isNull() == true); + + obj3 = obj3False; + RTTESTI_CHECK(obj3.m_fValue == false); + RTTESTI_CHECK(obj3.isNull() == false); + + /* setNull implies resetToDefault: */ + obj3 = obj3True; + RTTESTI_CHECK(obj3.m_fValue == true); + RTTESTI_CHECK(obj3.isNull() == false); + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + RTTESTI_CHECK(obj3.m_fValue == false); + + /* Copy constructors: */ + { + RTCRestBool obj3a(obj3True); + RTTESTI_CHECK(obj3a.m_fValue == true); + RTTESTI_CHECK(obj3a.isNull() == false); + } + { + RTCRestBool obj3b(obj3False); + RTTESTI_CHECK(obj3b.m_fValue == false); + RTTESTI_CHECK(obj3b.isNull() == false); + } + { + RTCRestBool obj3c(obj3Null); + RTTESTI_CHECK(obj3c.m_fValue == false); + RTTESTI_CHECK(obj3c.isNull() == true); + } + + /* Serialization to json: */ + const char *pszJson = toJson(&obj3True); + RTTESTI_CHECK_MSG(strcmp(pszJson, "true") == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3False); + RTTESTI_CHECK_MSG(strcmp(pszJson, "false") == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Null); + RTTESTI_CHECK_MSG(strcmp(pszJson, "null") == 0, ("pszJson=%s\n", pszJson)); + + /* Serialization to string. */ + RTCString str; + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3True.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:true"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3True.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("true"), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3False.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:false"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3False.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("false"), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Null.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:null"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3Null.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("null"), ("str=%s\n", str.c_str())); + } + + /* deserialize: */ + RTERRINFOSTATIC ErrInfo; + { + RTCRestBool obj4; + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "false", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "true", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == true); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == true); + + /* object goes to default state on failure: */ + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "0", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_BOOL); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == false); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + obj4.assignValue(true); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"false\"", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_BOOL); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == false); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "[ null ]", NULL, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_BOOL); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, "true", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == true); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "false", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.m_fValue = true; + RTTESTI_CHECK_RC(fromString(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == true); + + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, " TrUe ", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == true); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "\tfAlSe;", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "\r\nfAlSe\n;", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "\r\tNuLl\n;", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_fValue == false); + RTTESTI_CHECK(obj4.isNull() == true); + + RTTESTI_CHECK_RC(fromString(&obj4, "1", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_UNABLE_TO_PARSE_STRING_AS_BOOL); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + RTTESTI_CHECK_RC(fromString(&obj4, "0", NULL, RT_XSTR(__LINE__)), VERR_REST_UNABLE_TO_PARSE_STRING_AS_BOOL); + } +} + +class Int64Constants +{ +public: + Int64Constants() {} + const char *getSubName() const { return "RTCRestInt64"; } + int64_t getMin() const { return INT64_MIN; } + const char *getMinStr() const { return "-9223372036854775808"; } + int64_t getMax() const { return INT64_MAX; } + const char *getMaxStr() const { return "9223372036854775807"; } + const char *getTypeName() const { return "int64_t"; } + RTCRestObjectBase::kTypeClass getTypeClass() const { return RTCRestObjectBase::kTypeClass_Int64; } +}; + +class Int32Constants +{ +public: + Int32Constants() { } + const char *getSubName() const { return "RTCRestInt32"; } + int32_t getMin() const { return INT32_MIN; } + const char *getMinStr() const { return "-2147483648"; } + int32_t getMax() const { return INT32_MAX; } + const char *getMaxStr() const { return "2147483647"; } + const char *getTypeName() const { return "int32_t"; } + RTCRestObjectBase::kTypeClass getTypeClass() const { return RTCRestObjectBase::kTypeClass_Int32; } +}; + +class Int16Constants +{ +public: + Int16Constants() { } + const char *getSubName() const { return "RTCRestInt16"; } + int16_t getMin() const { return INT16_MIN; } + const char *getMinStr() const { return "-32768"; } + int16_t getMax() const { return INT16_MAX; } + const char *getMaxStr() const { return "32767"; } + const char *getTypeName() const { return "int16_t"; } + RTCRestObjectBase::kTypeClass getTypeClass() const { return RTCRestObjectBase::kTypeClass_Int16; } +}; + +template<typename RestType, typename IntType, typename ConstantClass> +void testInteger(void) +{ + ConstantClass const Consts; + RTTestSub(g_hTest, Consts.getSubName()); + + { + RestType obj1; + RTTESTI_CHECK(obj1.m_iValue == 0); + RTTESTI_CHECK(obj1.isNull() == false); + RTTESTI_CHECK(strcmp(obj1.typeName(), Consts.getTypeName()) == 0); + RTTESTI_CHECK(obj1.typeClass() == Consts.getTypeClass()); + } + + { + RestType obj2(2398); + RTTESTI_CHECK(obj2.m_iValue == 2398); + RTTESTI_CHECK(obj2.isNull() == false); + } + + { + RestType obj2(-7345); + RTTESTI_CHECK(obj2.m_iValue == -7345); + RTTESTI_CHECK(obj2.isNull() == false); + } + + { + /* Value assignments: */ + RestType obj3; + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + RTTESTI_CHECK(obj3.m_iValue == 0); + obj3.assignValue(-1); + RTTESTI_CHECK(obj3.m_iValue == -1); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + obj3.assignValue(42); + RTTESTI_CHECK(obj3.m_iValue == 42); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3.assignValue(Consts.getMax()); + RTTESTI_CHECK(obj3.m_iValue == Consts.getMax()); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3.assignValue(Consts.getMin()); + RTTESTI_CHECK(obj3.m_iValue == Consts.getMin()); + RTTESTI_CHECK(obj3.isNull() == false); + + /* Reset to default: */ + RTTESTI_CHECK_RC(obj3.resetToDefault(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_iValue == 0); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3.assignValue(42); + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.resetToDefault(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_iValue == 0); + RTTESTI_CHECK(obj3.isNull() == false); + + /* Copy assignments: */ + RestType obj3Max(Consts.getMax()); + RTTESTI_CHECK(obj3Max.m_iValue == Consts.getMax()); + RTTESTI_CHECK(obj3Max.isNull() == false); + RestType obj3Min(Consts.getMin()); + RTTESTI_CHECK(obj3Min.m_iValue == Consts.getMin()); + RTTESTI_CHECK(obj3Min.isNull() == false); + RestType obj3Null; + obj3Null.setNull(); + RTTESTI_CHECK(obj3Null.m_iValue == 0); + RTTESTI_CHECK(obj3Null.isNull() == true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Max), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_iValue == Consts.getMax()); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Null), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_iValue == 0); + RTTESTI_CHECK(obj3.isNull() == true); + + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Min), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_iValue == Consts.getMin()); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3 = obj3Null; + RTTESTI_CHECK(obj3.m_iValue == 0); + RTTESTI_CHECK(obj3.isNull() == true); + + obj3 = obj3Max; + RTTESTI_CHECK(obj3.m_iValue == Consts.getMax()); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3 = obj3Null; + RTTESTI_CHECK(obj3.m_iValue == 0); + RTTESTI_CHECK(obj3.isNull() == true); + + obj3 = obj3Min; + RTTESTI_CHECK(obj3.m_iValue == Consts.getMin()); + RTTESTI_CHECK(obj3.isNull() == false); + + /* setNull implies resetToDefault: */ + obj3 = obj3Max; + RTTESTI_CHECK(obj3.m_iValue == Consts.getMax()); + RTTESTI_CHECK(obj3.isNull() == false); + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + RTTESTI_CHECK(obj3.m_iValue == 0); + + /* Copy constructors: */ + { + RestType obj3a(obj3Max); + RTTESTI_CHECK(obj3a.m_iValue == Consts.getMax()); + RTTESTI_CHECK(obj3a.isNull() == false); + } + { + RestType obj3b(obj3Min); + RTTESTI_CHECK(obj3b.m_iValue == Consts.getMin()); + RTTESTI_CHECK(obj3b.isNull() == false); + } + { + RestType obj3c(obj3Null); + RTTESTI_CHECK(obj3c.m_iValue == 0); + RTTESTI_CHECK(obj3c.isNull() == true); + } + + /* Serialization to json: */ + const char *pszJson = toJson(&obj3Max); + RTTESTI_CHECK_MSG(strcmp(pszJson, Consts.getMaxStr()) == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Min); + RTTESTI_CHECK_MSG(strcmp(pszJson, Consts.getMinStr()) == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Null); + RTTESTI_CHECK_MSG(strcmp(pszJson, "null") == 0, ("pszJson=%s\n", pszJson)); + + /* Serialization to string. */ + RTCString str; + RTCString strExpect; + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Max.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + strExpect.printf("lead-in:%s", Consts.getMaxStr()); + RTTESTI_CHECK_MSG(str.equals(strExpect), ("str=%s strExpect=%s\n", str.c_str(), strExpect.c_str())); + RTTESTI_CHECK_RC(obj3Max.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals(Consts.getMaxStr()), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Min.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + strExpect.printf("lead-in:%s", Consts.getMinStr()); + RTTESTI_CHECK_MSG(str.equals(strExpect), ("str=%s strExpect=%s\n", str.c_str(), strExpect.c_str())); + RTTESTI_CHECK_RC(obj3Min.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals(Consts.getMinStr()), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Null.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:null"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3Null.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("null"), ("str=%s\n", str.c_str())); + } + + /* deserialize: */ + RTERRINFOSTATIC ErrInfo; + { + /* from json: */ + RestType obj4; + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "42", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == 42); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "-22", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == -22); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, Consts.getMaxStr(), &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == Consts.getMax()); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, Consts.getMinStr(), &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == Consts.getMin()); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == 0); + RTTESTI_CHECK(obj4.isNull() == true); + + /* object goes to default state on failure: */ + obj4.assignValue(Consts.getMin()); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "0.0", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_INTEGER); + RTTESTI_CHECK(obj4.m_iValue == 0); + RTTESTI_CHECK(obj4.isNull() == false); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + obj4.assignValue(Consts.getMax()); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"false\"", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_INTEGER); + RTTESTI_CHECK(obj4.m_iValue == 0); + RTTESTI_CHECK(obj4.isNull() == false); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "[ null ]", NULL, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_INTEGER); + RTTESTI_CHECK(obj4.m_iValue == 0); + RTTESTI_CHECK(obj4.isNull() == false); + + /* from string: */ + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, "22", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == 22); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "-42", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == -42); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, Consts.getMaxStr(), &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == Consts.getMax()); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, Consts.getMinStr(), &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == Consts.getMin()); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.m_iValue = 33; + RTTESTI_CHECK_RC(fromString(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == 0); + RTTESTI_CHECK(obj4.isNull() == true); + + obj4.m_iValue = 33; + RTTESTI_CHECK_RC(fromString(&obj4, " nULl;", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == 0); + RTTESTI_CHECK(obj4.isNull() == true); + + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, " 0x42 ", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == 0x42); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "\t010\t", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == 8); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "\r\t0X4FDB\t", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_iValue == 0x4fdb); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "1.1", &ErrInfo, RT_XSTR(__LINE__)), VERR_TRAILING_CHARS); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + RTTESTI_CHECK_RC(fromString(&obj4, "false", NULL, RT_XSTR(__LINE__)), VERR_NO_DIGITS); + } +} + + +void testDouble(void) +{ + RTTestSub(g_hTest, "RTCRestDouble"); +#define TST_DBL_MAX (1.79769313486231571e+308) +#define TST_DBL_MIN (2.22507385850720138e-308) +#define TST_DBL_MAX_STRING1 "1.79769313486231571e+308" +#define TST_DBL_MAX_STRING2 "1.7976931348623157e+308" +#define TST_DBL_MAX_EQUAL(a_psz) ( strcmp(a_psz, TST_DBL_MAX_STRING1) == 0 || strcmp(a_psz, TST_DBL_MAX_STRING2) == 0 ) +#define TST_DBL_MIN_STRING1 "2.22507385850720138e-308" +#define TST_DBL_MIN_STRING2 "2.2250738585072014e-308" +#define TST_DBL_MIN_EQUAL(a_psz) ( strcmp(a_psz, TST_DBL_MIN_STRING1) == 0 || strcmp(a_psz, TST_DBL_MIN_STRING2) == 0 ) + + { + RTCRestDouble obj1; + RTTESTI_CHECK(obj1.m_rdValue == 0.0); + RTTESTI_CHECK(obj1.isNull() == false); + RTTESTI_CHECK(strcmp(obj1.typeName(), "double") == 0); + RTTESTI_CHECK(obj1.typeClass() == RTCRestObjectBase::kTypeClass_Double); + } + + { + RTCRestDouble obj2(2398.1); + RTTESTI_CHECK(obj2.m_rdValue == 2398.1); + RTTESTI_CHECK(obj2.isNull() == false); + } + + { + RTCRestDouble obj2(-7345.2); + RTTESTI_CHECK(obj2.m_rdValue == -7345.2); + RTTESTI_CHECK(obj2.isNull() == false); + } + + { + /* Value assignments: */ + RTCRestDouble obj3; + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + RTTESTI_CHECK(obj3.m_rdValue == 0.0); + obj3.assignValue(-1.0); + RTTESTI_CHECK(obj3.m_rdValue == -1.0); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + obj3.assignValue(42.42); + RTTESTI_CHECK(obj3.m_rdValue == 42.42); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3.assignValue(TST_DBL_MAX); + RTTESTI_CHECK(obj3.m_rdValue == TST_DBL_MAX); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3.assignValue(TST_DBL_MIN); + RTTESTI_CHECK(obj3.m_rdValue == TST_DBL_MIN); + RTTESTI_CHECK(obj3.isNull() == false); + + /* Reset to default: */ + RTTESTI_CHECK_RC(obj3.resetToDefault(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_rdValue == 0.0); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3.assignValue(42); + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.resetToDefault(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_rdValue == 0.0); + RTTESTI_CHECK(obj3.isNull() == false); + + /* Copy assignments: */ + RTCRestDouble obj3Max(TST_DBL_MAX); + RTTESTI_CHECK(obj3Max.m_rdValue == TST_DBL_MAX); + RTTESTI_CHECK(obj3Max.isNull() == false); + RTCRestDouble obj3Min(TST_DBL_MIN); + RTTESTI_CHECK(obj3Min.m_rdValue == TST_DBL_MIN); + RTTESTI_CHECK(obj3Min.isNull() == false); + RTCRestDouble obj3Null; + obj3Null.setNull(); + RTTESTI_CHECK(obj3Null.m_rdValue == 0.0); + RTTESTI_CHECK(obj3Null.isNull() == true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Max), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_rdValue == TST_DBL_MAX); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Null), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_rdValue == 0.0); + RTTESTI_CHECK(obj3.isNull() == true); + + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Min), VINF_SUCCESS); + RTTESTI_CHECK(obj3.m_rdValue == TST_DBL_MIN); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3 = obj3Null; + RTTESTI_CHECK(obj3.m_rdValue == 0.0); + RTTESTI_CHECK(obj3.isNull() == true); + + obj3 = obj3Max; + RTTESTI_CHECK(obj3.m_rdValue == TST_DBL_MAX); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3 = obj3Null; + RTTESTI_CHECK(obj3.m_rdValue == 0.0); + RTTESTI_CHECK(obj3.isNull() == true); + + obj3 = obj3Min; + RTTESTI_CHECK(obj3.m_rdValue == TST_DBL_MIN); + RTTESTI_CHECK(obj3.isNull() == false); + + /* setNull implies resetToDefault: */ + obj3 = obj3Max; + RTTESTI_CHECK(obj3.m_rdValue == TST_DBL_MAX); + RTTESTI_CHECK(obj3.isNull() == false); + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + RTTESTI_CHECK(obj3.m_rdValue == 0.0); + + /* Copy constructors: */ + { + RTCRestDouble obj3a(obj3Max); + RTTESTI_CHECK(obj3a.m_rdValue == TST_DBL_MAX); + RTTESTI_CHECK(obj3a.isNull() == false); + } + { + RTCRestDouble obj3b(obj3Min); + RTTESTI_CHECK(obj3b.m_rdValue == TST_DBL_MIN); + RTTESTI_CHECK(obj3b.isNull() == false); + } + { + RTCRestDouble obj3c(obj3Null); + RTTESTI_CHECK(obj3c.m_rdValue == 0.0); + RTTESTI_CHECK(obj3c.isNull() == true); + } + + /* Serialization to json: */ + const char *pszJson = toJson(&obj3Max); + RTTESTI_CHECK_MSG(TST_DBL_MAX_EQUAL(pszJson), ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Min); + RTTESTI_CHECK_MSG(TST_DBL_MIN_EQUAL(pszJson), ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Null); + RTTESTI_CHECK_MSG(strcmp(pszJson, "null") == 0, ("pszJson=%s\n", pszJson)); + + /* Serialization to string. */ + RTCString str; + RTCString strExpect1; + RTCString strExpect2; + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Max.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + strExpect1.printf("lead-in:%s", TST_DBL_MAX_STRING1); + strExpect2.printf("lead-in:%s", TST_DBL_MAX_STRING2); + RTTESTI_CHECK_MSG(str.equals(strExpect1) || str.equals(strExpect2), + ("str=%s strExpect1=%s strExpect2=%s\n", str.c_str(), strExpect1.c_str(), strExpect2.c_str())); + RTTESTI_CHECK_RC(obj3Max.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(TST_DBL_MAX_EQUAL(str.c_str()), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Min.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + strExpect1.printf("lead-in:%s", TST_DBL_MIN_STRING1); + strExpect2.printf("lead-in:%s", TST_DBL_MIN_STRING2); + RTTESTI_CHECK_MSG(str.equals(strExpect1) || str.equals(strExpect2), + ("str=%s strExpect1=%s strExpect2=%s\n", str.c_str(), strExpect1.c_str(), strExpect2.c_str())); + RTTESTI_CHECK_RC(obj3Min.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(TST_DBL_MIN_EQUAL(str.c_str()), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Null.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:null"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3Null.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("null"), ("str=%s\n", str.c_str())); + } + + /* deserialize: */ + RTERRINFOSTATIC ErrInfo; + { + /* Some linux systems and probably all solaris fail to parse the longer MIN string, so just detect and skip. */ + bool fGroksMinString = true; +#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) + RTJSONVAL hTmpValue = NIL_RTJSONVAL; + int rcTmp = RTJsonParseFromString(&hTmpValue, TST_DBL_MIN_STRING1, NULL); + RTJsonValueRelease(hTmpValue); + if (rcTmp == VERR_INVALID_PARAMETER) + fGroksMinString = false; +#endif + + /* from json: */ + RTCRestDouble obj4; + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "42.42", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == 42.42); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "-22.22", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == -22.22); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, TST_DBL_MAX_STRING1, &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == TST_DBL_MAX); + RTTESTI_CHECK(obj4.isNull() == false); + + if (fGroksMinString) + { + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, TST_DBL_MIN_STRING1, &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == TST_DBL_MIN); + RTTESTI_CHECK(obj4.isNull() == false); + } + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == 0.0); + RTTESTI_CHECK(obj4.isNull() == true); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "14323", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == 14323.0); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "-234875", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == -234875.0); + RTTESTI_CHECK(obj4.isNull() == false); + + /* object goes to default state on failure: */ + obj4.assignValue(TST_DBL_MIN); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "false", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_DOUBLE); + RTTESTI_CHECK(obj4.m_rdValue == 0.0); + RTTESTI_CHECK(obj4.isNull() == false); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + obj4.assignValue(TST_DBL_MAX); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"false\"", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_DOUBLE); + RTTESTI_CHECK(obj4.m_rdValue == 0.0); + RTTESTI_CHECK(obj4.isNull() == false); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "[ null ]", NULL, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_DOUBLE); + RTTESTI_CHECK(obj4.m_rdValue == 0.0); + RTTESTI_CHECK(obj4.isNull() == false); + + /* from string: */ + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, "22.42", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == 22.42); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "-42.22", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == -42.22); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, TST_DBL_MAX_STRING1, &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == TST_DBL_MAX); + RTTESTI_CHECK(obj4.isNull() == false); + + if (fGroksMinString) + { + RTTESTI_CHECK_RC(fromString(&obj4, TST_DBL_MIN_STRING1, &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == TST_DBL_MIN); + RTTESTI_CHECK(obj4.isNull() == false); + } + + obj4.m_rdValue = 33.33; + RTTESTI_CHECK_RC(fromString(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == 0.0); + RTTESTI_CHECK(obj4.isNull() == true); + + obj4.m_rdValue = 33.33; + RTTESTI_CHECK_RC(fromString(&obj4, " nULl;", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == 0.0); + RTTESTI_CHECK(obj4.isNull() == true); + + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, " 42.22 ", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == 42.22); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "\t010\t", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue ==10.0); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "\r\t03495.344\t\r\n", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == 3495.344); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "1.1;", &ErrInfo, RT_XSTR(__LINE__)), VERR_TRAILING_CHARS); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + RTTESTI_CHECK_RC(fromString(&obj4, "false", NULL, RT_XSTR(__LINE__)), VERR_NO_DIGITS); + +#if defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) + RTTESTI_CHECK_RC(fromString(&obj4, " 0x42 ", &ErrInfo, RT_XSTR(__LINE__)), VERR_TRAILING_CHARS); + RTTESTI_CHECK(obj4.m_rdValue == 0.0); +#else + RTTESTI_CHECK_RC(fromString(&obj4, " 0x42 ", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.m_rdValue == 66.0); +#endif + RTTESTI_CHECK(obj4.isNull() == false); + } +} + + +void testString(const char *pszDummy, ...) +{ + RTTestSub(g_hTest, "RTCRestString"); + + { + RTCRestString obj1; + RTTESTI_CHECK(obj1.isEmpty()); + RTTESTI_CHECK(obj1.isNull() == false); + RTTESTI_CHECK(strcmp(obj1.typeName(), "RTCString") == 0); + RTTESTI_CHECK(obj1.typeClass() == RTCRestObjectBase::kTypeClass_String); + } + + { + RTCRestString obj2(RTCString("2398.1")); + RTTESTI_CHECK(obj2 == "2398.1"); + RTTESTI_CHECK(obj2.isNull() == false); + } + + { + RTCRestString obj2("-7345.2"); + RTTESTI_CHECK(obj2 == "-7345.2"); + RTTESTI_CHECK(obj2.isNull() == false); + } + + { + /* Value assignments: */ + RTCRestString obj3; + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + RTTESTI_CHECK(obj3.isEmpty()); + obj3 = "-1.0"; + RTTESTI_CHECK(obj3 == "-1.0"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + obj3 = RTCString("-2.0"); + RTTESTI_CHECK(obj3 == "-2.0"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + obj3 = RTCRestString("-3.0"); + RTTESTI_CHECK(obj3 == "-3.0"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignNoThrow(RTCRestString("4.0")), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == "4.0"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignNoThrow("-4.0"), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == "-4.0"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignNoThrow(RTCRestString("0123456789"), 3, 5), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == "34567"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignNoThrow("0123456789", 4), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == "0123"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignNoThrow(8, 'x'), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == "xxxxxxxx"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.printfNoThrow("%d%s%d", 42, "asdf", 22), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == "42asdf22"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + va_list va; + va_start(va, pszDummy); + RTTESTI_CHECK_RC(obj3.printfVNoThrow("asdf", va), VINF_SUCCESS); + va_end(va); + RTTESTI_CHECK(obj3 == "asdf"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + obj3.assign(RTCRestString("4.0")); + RTTESTI_CHECK(obj3 == "4.0"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + obj3.assign("-4.0"); + RTTESTI_CHECK(obj3 == "-4.0"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + obj3.assign(RTCRestString("0123456789"), 3, 5); + RTTESTI_CHECK(obj3 == "34567"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + obj3.assign("0123456789", 4); + RTTESTI_CHECK(obj3 == "0123"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + obj3.assign(8, 'x'); + RTTESTI_CHECK(obj3 == "xxxxxxxx"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + obj3.printf("%d%s%d", 42, "asdf", 22); + RTTESTI_CHECK(obj3 == "42asdf22"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + va_start(va, pszDummy); + obj3.printfV("asdf", va); + va_end(va); + RTTESTI_CHECK(obj3 == "asdf"); + RTTESTI_CHECK(obj3.isNull() == false); + + /* Reset to default: */ + RTTESTI_CHECK_RC(obj3.resetToDefault(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isEmpty()); + RTTESTI_CHECK(obj3.isNull() == false); + + obj3 = "1"; + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.resetToDefault(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isEmpty()); + RTTESTI_CHECK(obj3.isNull() == false); + + /* Copy assignments: */ + RTCRestString const obj3Max("max"); + RTTESTI_CHECK(obj3Max == "max"); + RTTESTI_CHECK(obj3Max.isNull() == false); + RTCRestString obj3Null; + obj3Null.setNull(); + RTTESTI_CHECK(obj3Null.isEmpty()); + RTTESTI_CHECK(obj3Null.isNull() == true); + RTCRestString obj3Empty; + RTTESTI_CHECK(obj3Empty.isEmpty()); + RTTESTI_CHECK(obj3Empty.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Max), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == "max"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Null), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isEmpty()); + RTTESTI_CHECK(obj3.isNull() == true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Empty), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == ""); + RTTESTI_CHECK(obj3.isEmpty()); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignCopy(RTCString("11.0")), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == "11.0"); + RTTESTI_CHECK(obj3.isNull() == false); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true && obj3.isEmpty()); + RTTESTI_CHECK_RC(obj3.assignCopy("12.0"), VINF_SUCCESS); + RTTESTI_CHECK(obj3 == "12.0"); + RTTESTI_CHECK(obj3.isNull() == false); + + /* setNull implies resetToDefault: */ + obj3 = obj3Max; + RTTESTI_CHECK(obj3 == "max"); + RTTESTI_CHECK(obj3.isNull() == false); + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(obj3.isNull() == true); + RTTESTI_CHECK(obj3.isEmpty()); + + /* Copy constructors: */ + { + RTCRestString obj3a(obj3Max); + RTTESTI_CHECK(obj3a == "max"); + RTTESTI_CHECK(obj3a.isNull() == false); + } + { + RTCRestString const obj3c(obj3Null); + RTTESTI_CHECK(obj3c.isEmpty()); + RTTESTI_CHECK(obj3c.isNull() == true); + } + + /* Serialization to json: */ + const char *pszJson = toJson(&obj3Max); + RTTESTI_CHECK_MSG(strcmp(pszJson, "\"max\"") == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Null); + RTTESTI_CHECK_MSG(strcmp(pszJson, "null") == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Empty); + RTTESTI_CHECK_MSG(strcmp(pszJson, "\"\"") == 0, ("pszJson=%s\n", pszJson)); + + /* Serialization to string. */ + RTCString str; + RTCString strExpect; + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Max.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:max"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3Max.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("max"), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Empty.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3Empty.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals(""), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Null.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3Null.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals(""), ("str=%s\n", str.c_str())); + } + + /* deserialize: */ + RTERRINFOSTATIC ErrInfo; + { + /* from json: */ + RTCRestString obj4; + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"42.42\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4 == "42.42"); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"-22.22\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4 == "-22.22"); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"maximum\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4 == "maximum"); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4.isEmpty()); + RTTESTI_CHECK(obj4.isNull() == true); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"\\u0020\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4 == " "); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"\\u004f\\u004D\\u0047\\u0021 :-)\"", + &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4 == "OMG! :-)"); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"42:\\uD801\\udC37\\ud852\\uDf62:42\"", /* U+10437 U+24B62 */ + &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4 == "42:" "\xf0\x90\x90\xb7" "\xf0\xa4\xad\xa2" ":42"); + RTTESTI_CHECK(obj4.isNull() == false); + + /* object goes to default state on failure: */ + obj4 = "asdf"; + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "false", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_STRING); + RTTESTI_CHECK(obj4.isEmpty()); + RTTESTI_CHECK(obj4.isNull() == false); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + obj4 = "asdf"; + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "1", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_STRING); + RTTESTI_CHECK(obj4.isEmpty()); + RTTESTI_CHECK(obj4.isNull() == false); + RTTESTI_CHECK(RTErrInfoIsSet(&ErrInfo.Core)); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "[ null ]", NULL, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_STRING); + RTTESTI_CHECK(obj4.isEmpty()); + RTTESTI_CHECK(obj4.isNull() == false); + + /* from string: */ + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, "22.42", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4 == "22.42"); + RTTESTI_CHECK(obj4.isNull() == false); + + RTTESTI_CHECK_RC(fromString(&obj4, "-42.22", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4 == "-42.22"); + RTTESTI_CHECK(obj4.isNull() == false); + + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + RTTESTI_CHECK(obj4 == "null"); + RTTESTI_CHECK(obj4.isNull() == false); + } +} + + +void testDate() +{ + RTTestSub(g_hTest, "RTCRestDate"); + int64_t const iRecent = INT64_C(1536580687739632500); + int64_t const iRecentSec = INT64_C(1536580687000000000); + RTTIMESPEC TimeSpec; + +#define CHECK_DATE(a_obj, a_fNull, a_fOkay, a_i64Nano, a_sz, a_fUtc) \ + do { \ + RTTESTI_CHECK((a_obj).isOkay() == (a_fOkay)); \ + if ((a_obj).getEpochNano() != (a_i64Nano)) \ + RTTestIFailed("line " RT_XSTR(__LINE__) ": getEpochNano=%RI64, expected %RI64", (a_obj).getEpochNano(), (int64_t)(a_i64Nano)); \ + if (!(a_obj).getString().equals(a_sz)) \ + RTTestIFailed("line " RT_XSTR(__LINE__) ": getString=%s, expected %s", (a_obj).getString().c_str(), a_sz); \ + RTTESTI_CHECK((a_obj).isUtc() == (a_fUtc)); \ + RTTESTI_CHECK((a_obj).isNull() == (a_fNull)); \ + } while (0) +#define CHECK_DATE_FMT(a_obj, a_fNull, a_fOkay, a_i64Nano, a_sz, a_fUtc, a_enmFormat) \ + do { \ + CHECK_DATE(a_obj, a_fNull, a_fOkay, a_i64Nano, a_sz, a_fUtc); \ + if ((a_obj).getFormat() != (a_enmFormat)) \ + RTTestIFailed("line " RT_XSTR(__LINE__) ": getFormat=%d, expected %d (%s)", (a_obj).getFormat(), (a_enmFormat), #a_enmFormat); \ + } while (0) + + { + RTCRestDate obj1; + CHECK_DATE(obj1, true, false, 0, "", true); + RTTESTI_CHECK(strcmp(obj1.typeName(), "RTCRestDate") == 0); + RTTESTI_CHECK(obj1.typeClass() == RTCRestObjectBase::kTypeClass_Date); + } + + { + /* Value assignments: */ + RTCRestDate obj3; + RTTESTI_CHECK_RC(obj3.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc3339), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "1970-01-01T00:00:00Z", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + CHECK_DATE(obj3, true, false, 0, "", true); + RTTESTI_CHECK_RC(obj3.assignValueRfc3339(RTTimeSpecSetNano(&TimeSpec, 0)), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "1970-01-01T00:00:00Z", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + CHECK_DATE(obj3, true, false, 0, "", true); + RTTESTI_CHECK_RC(obj3.assignValueRfc2822(RTTimeSpecSetNano(&TimeSpec, 0)), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "Thu, 1 Jan 1970 00:00:00 -0000", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignValueRfc7131(RTTimeSpecSetNano(&TimeSpec, 0)), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "Thu, 1 Jan 1970 00:00:00 GMT", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc2822), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "Thu, 1 Jan 1970 00:00:00 -0000", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc7131), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "Thu, 1 Jan 1970 00:00:00 GMT", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc3339_Fraction_9), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "1970-01-01T00:00:00.000000000Z", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc3339_Fraction_6), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "1970-01-01T00:00:00.000000Z", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc3339_Fraction_3), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "1970-01-01T00:00:00.000Z", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc3339_Fraction_2), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "1970-01-01T00:00:00.00Z", true); + + /* Format changes: */ + RTTESTI_CHECK_RC(obj3.assignValue(RTTimeSpecSetNano(&TimeSpec, 59123456789), RTCRestDate::kFormat_Rfc3339_Fraction_9), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 59123456789, "1970-01-01T00:00:59.123456789Z", true); + RTTESTI_CHECK_RC(obj3.setFormat(RTCRestDate::kFormat_Rfc2822), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 59123456789, "Thu, 1 Jan 1970 00:00:59 -0000", true); + RTTESTI_CHECK_RC(obj3.setFormat(RTCRestDate::kFormat_Rfc7131), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 59123456789, "Thu, 1 Jan 1970 00:00:59 GMT", true); + RTTESTI_CHECK_RC(obj3.setFormat(RTCRestDate::kFormat_Rfc3339), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 59123456789, "1970-01-01T00:00:59Z", true); + RTTESTI_CHECK_RC(obj3.setFormat(RTCRestDate::kFormat_Rfc3339_Fraction_2), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 59123456789, "1970-01-01T00:00:59.12Z", true); + RTTESTI_CHECK_RC(obj3.setFormat(RTCRestDate::kFormat_Rfc3339_Fraction_3), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 59123456789, "1970-01-01T00:00:59.123Z", true); + RTTESTI_CHECK_RC(obj3.setFormat(RTCRestDate::kFormat_Rfc3339_Fraction_6), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 59123456789, "1970-01-01T00:00:59.123456Z", true); + RTTESTI_CHECK_RC(obj3.setFormat(RTCRestDate::kFormat_Rfc3339_Fraction_9), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 59123456789, "1970-01-01T00:00:59.123456789Z", true); + + /* Reset to default and setNull works identically: */ + RTTESTI_CHECK_RC(obj3.resetToDefault(), VINF_SUCCESS); + CHECK_DATE(obj3, true, false, 0, "", true); + + RTTESTI_CHECK_RC(obj3.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc3339_Fraction_2), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "1970-01-01T00:00:00.00Z", true); + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + CHECK_DATE(obj3, true, false, 0, "", true); + + /* Copy assignments: */ + RTCRestDate obj3Epoch_3339_9; + RTTESTI_CHECK_RC(obj3Epoch_3339_9.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc3339_Fraction_9), VINF_SUCCESS); + CHECK_DATE(obj3Epoch_3339_9, false, true, 0, "1970-01-01T00:00:00.000000000Z", true); + + RTCRestDate obj3Epoch_7131; + RTTESTI_CHECK_RC(obj3Epoch_7131.assignValue(RTTimeSpecSetNano(&TimeSpec, 0), RTCRestDate::kFormat_Rfc7131), VINF_SUCCESS); + CHECK_DATE(obj3Epoch_7131, false, true, 0, "Thu, 1 Jan 1970 00:00:00 GMT", true); + + RTCRestDate obj3Recent_3339; + RTTESTI_CHECK_RC(obj3Recent_3339.assignValue(RTTimeSpecSetNano(&TimeSpec, iRecent), RTCRestDate::kFormat_Rfc3339), VINF_SUCCESS); + CHECK_DATE(obj3Recent_3339, false, true, iRecent, "2018-09-10T11:58:07Z", true); + + RTCRestDate obj3Recent_2822; + RTTESTI_CHECK_RC(obj3Recent_2822.assignValue(RTTimeSpecSetNano(&TimeSpec, iRecent), RTCRestDate::kFormat_Rfc2822), VINF_SUCCESS); + CHECK_DATE(obj3Recent_2822, false, true, iRecent, "Mon, 10 Sep 2018 11:58:07 -0000", true); + + RTCRestDate const obj3Null; + CHECK_DATE(obj3Null, true, false, 0, "", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Epoch_3339_9), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "1970-01-01T00:00:00.000000000Z", true); + + RTTESTI_CHECK_RC(obj3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Epoch_7131), VINF_SUCCESS); + CHECK_DATE(obj3, false, true, 0, "Thu, 1 Jan 1970 00:00:00 GMT", true); + + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Recent_3339), VINF_SUCCESS); + CHECK_DATE(obj3Recent_2822, false, true, iRecent, "Mon, 10 Sep 2018 11:58:07 -0000", true); + + RTTESTI_CHECK_RC(obj3.assignCopy(obj3Null), VINF_SUCCESS); + CHECK_DATE(obj3, true, false, 0, "", true); + + obj3 = obj3Recent_2822; + CHECK_DATE(obj3Recent_2822, false, true, iRecent, "Mon, 10 Sep 2018 11:58:07 -0000", true); + + obj3 = obj3Epoch_3339_9; + CHECK_DATE(obj3, false, true, 0, "1970-01-01T00:00:00.000000000Z", true); + + obj3 = obj3Null; + CHECK_DATE(obj3, true, false, 0, "", true); + + /* Copy constructors: */ + { + RTCRestDate obj3a(obj3Epoch_3339_9); + CHECK_DATE(obj3a, false, true, 0, "1970-01-01T00:00:00.000000000Z", true); + } + { + RTCRestDate obj3b(obj3Epoch_7131); + CHECK_DATE(obj3b, false, true, 0, "Thu, 1 Jan 1970 00:00:00 GMT", true); + } + { + RTCRestDate obj3c(obj3Recent_3339); + CHECK_DATE(obj3Recent_3339, false, true, iRecent, "2018-09-10T11:58:07Z", true); + } + { + RTCRestDate obj3d(obj3Recent_2822); + CHECK_DATE(obj3d, false, true, iRecent, "Mon, 10 Sep 2018 11:58:07 -0000", true); + } + { + RTCRestDate obj3e(obj3Null); + CHECK_DATE(obj3e, true, false, 0, "", true); + } + + /* Serialization to json: */ + const char *pszJson = toJson(&obj3Epoch_3339_9); + RTTESTI_CHECK_MSG(strcmp(pszJson, "\"1970-01-01T00:00:00.000000000Z\"") == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Epoch_7131); + RTTESTI_CHECK_MSG(strcmp(pszJson, "\"Thu, 1 Jan 1970 00:00:00 GMT\"") == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Recent_3339); + RTTESTI_CHECK_MSG(strcmp(pszJson, "\"2018-09-10T11:58:07Z\"") == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Recent_2822); + RTTESTI_CHECK_MSG(strcmp(pszJson, "\"Mon, 10 Sep 2018 11:58:07 -0000\"") == 0, ("pszJson=%s\n", pszJson)); + pszJson = toJson(&obj3Null); + RTTESTI_CHECK_MSG(strcmp(pszJson, "null") == 0, ("pszJson=%s\n", pszJson)); + + /* Serialization to string. */ + RTCString str; + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Epoch_7131.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:Thu, 1 Jan 1970 00:00:00 GMT"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3Epoch_7131.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("Thu, 1 Jan 1970 00:00:00 GMT"), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Recent_3339.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:2018-09-10T11:58:07Z"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3Recent_3339.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("2018-09-10T11:58:07Z"), ("str=%s\n", str.c_str())); + + str = "lead-in:"; + RTTESTI_CHECK_RC(obj3Null.toString(&str, RTCRestObjectBase::kToString_Append), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("lead-in:null"), ("str=%s\n", str.c_str())); + RTTESTI_CHECK_RC(obj3Null.toString(&str), VINF_SUCCESS); + RTTESTI_CHECK_MSG(str.equals("null"), ("str=%s\n", str.c_str())); + } + + /* deserialize: */ + RTERRINFOSTATIC ErrInfo; + { + RTCRestDate obj4; + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"Thu, 1 Jan 1970 00:00:00 GMT\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, 0, "Thu, 1 Jan 1970 00:00:00 GMT", true, RTCRestDate::kFormat_Rfc7131); + + obj4.setNull(); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"Thu, 1 Jan 1970 00:00:00.0000 GMT\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, 0, "Thu, 1 Jan 1970 00:00:00.0000 GMT", true, RTCRestDate::kFormat_Rfc7131); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"1 Jan 1970 00:00:00 GMT\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, 0, "1 Jan 1970 00:00:00 GMT", true, RTCRestDate::kFormat_Rfc7131); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"1 Jan 1970 00:00:00\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, 0, "1 Jan 1970 00:00:00", false, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"1 Jan 070 00:00:00\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, 0, "1 Jan 070 00:00:00", false, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"2018-09-10T11:58:07Z\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, iRecentSec , "2018-09-10T11:58:07Z", true, RTCRestDate::kFormat_Rfc3339); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"1 Jan 70 00:00:00\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, 0, "1 Jan 70 00:00:00", false, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"2018-09-10T11:58:07.739632500Z\"", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, iRecent, "2018-09-10T11:58:07.739632500Z", true, RTCRestDate::kFormat_Rfc3339_Fraction_9); + + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, true, false, 0, "", true, RTCRestDate::kFormat_Rfc3339_Fraction_9); + + /* object goes to default state if not string and to non-okay if string: */ + RTTESTI_CHECK_RC(obj4.assignValue(RTTimeSpecSetNano(&TimeSpec, iRecent), RTCRestDate::kFormat_Rfc2822), VINF_SUCCESS); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "true", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_DATE); + CHECK_DATE_FMT(obj4, true, false, 0, "", true, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(obj4.assignValue(RTTimeSpecSetNano(&TimeSpec, iRecent), RTCRestDate::kFormat_Rfc2822), VINF_SUCCESS); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"string\"", &ErrInfo, RT_XSTR(__LINE__)), VWRN_REST_UNABLE_TO_DECODE_DATE); + CHECK_DATE_FMT(obj4, false, false, 0, "string", false, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(obj4.assignValue(RTTimeSpecSetNano(&TimeSpec, iRecent), RTCRestDate::kFormat_Rfc2822), VINF_SUCCESS); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "\"0x199 string\"", &ErrInfo, RT_XSTR(__LINE__)), VWRN_REST_UNABLE_TO_DECODE_DATE); + CHECK_DATE_FMT(obj4, false, false, 0, "0x199 string", false, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(obj4.assignValue(RTTimeSpecSetNano(&TimeSpec, iRecent), RTCRestDate::kFormat_Rfc2822), VINF_SUCCESS); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "[ null ]", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_DATE); + CHECK_DATE_FMT(obj4, true, false, 0, "", true, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(obj4.assignValue(RTTimeSpecSetNano(&TimeSpec, iRecent), RTCRestDate::kFormat_Rfc2822), VINF_SUCCESS); + RTTESTI_CHECK_RC(deserializeFromJson(&obj4, "{ \"foo\": 1 }", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_WRONG_JSON_TYPE_FOR_DATE); + CHECK_DATE_FMT(obj4, true, false, 0, "", true, RTCRestDate::kFormat_Rfc2822); + + /* From string: */ + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, "Thu, 1 Jan 1970 00:00:00 GMT", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, 0, "Thu, 1 Jan 1970 00:00:00 GMT", true, RTCRestDate::kFormat_Rfc7131); + + RTTESTI_CHECK_RC(fromString(&obj4, "Mon, 10 Sep 2018 11:58:07 -0000", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, iRecentSec, "Mon, 10 Sep 2018 11:58:07 -0000", true, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(fromString(&obj4, "\t\n\rnull;\r\n\t", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, true, false, 0, "", true, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(fromString(&obj4, "Mon, 10 Sep 2018 11:58:07 +0000", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, iRecentSec, "Mon, 10 Sep 2018 11:58:07 +0000", false, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(fromString(&obj4, "1970-01-01T00:00:00.000000000Z", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, 0, "1970-01-01T00:00:00.000000000Z", true, RTCRestDate::kFormat_Rfc3339_Fraction_9); + + RTTESTI_CHECK_RC(fromString(&obj4, "10 Sep 2018 11:58:07 -0000", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, iRecentSec, "10 Sep 2018 11:58:07 -0000", true, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(fromString(&obj4, "null", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, true, false, 0, "", true, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(fromString(&obj4, "Mon, 10 Sep 18 11:58:07 -0000", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, iRecentSec, "Mon, 10 Sep 18 11:58:07 -0000", true, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(fromString(&obj4, "fa;se", &ErrInfo, RT_XSTR(__LINE__)), VERR_REST_UNABLE_TO_DECODE_DATE); + CHECK_DATE_FMT(obj4, false, false, 0, "fa;se", false, RTCRestDate::kFormat_Rfc2822); + + RTTESTI_CHECK_RC(fromString(&obj4, "10 Sep 18 11:58:07", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, iRecentSec, "10 Sep 18 11:58:07", false, RTCRestDate::kFormat_Rfc2822); + + obj4.setNull(); + RTTESTI_CHECK_RC(fromString(&obj4, "10 Sep 118 11:58:07", &ErrInfo, RT_XSTR(__LINE__)), VINF_SUCCESS); + CHECK_DATE_FMT(obj4, false, true, iRecentSec, "10 Sep 118 11:58:07", false, RTCRestDate::kFormat_Rfc2822); + } +} + + +/** Wraps RTCRestInt16 to check for leaks. */ +class MyRestInt16 : public RTCRestInt16 +{ +public: + static size_t s_cInstances; + MyRestInt16() : RTCRestInt16() { s_cInstances++; /*printf("%p: default %02u; cInstances %zu\n", this, m_iValue, s_cInstances);*/ } + MyRestInt16(MyRestInt16 const &a_rThat) : RTCRestInt16(a_rThat) { s_cInstances++; /*printf("%p: copy %02u; cInstances %zu\n", this, m_iValue, s_cInstances);*/ } + MyRestInt16(int16_t a_iValue) : RTCRestInt16(a_iValue) { s_cInstances++; /*printf("%p: value %02u; cInstances %zu\n", this, m_iValue, s_cInstances);*/ } + ~MyRestInt16() { s_cInstances--; /*printf("%p: delete %02u; cInstances %zu\n", this, m_iValue, s_cInstances);*/ } + virtual RTCRestObjectBase *baseClone() const RT_NOEXCEPT RT_OVERRIDE { /*printf("clone\n");*/ return new MyRestInt16(*this); } +}; + +size_t MyRestInt16::s_cInstances = 0; + + +static void verifyArray(RTCRestArray<MyRestInt16> const &rArray, int iLine, unsigned cElements, ...) +{ + if (rArray.size() != cElements) + RTTestIFailed("line %u: size() -> %zu, expected %u", iLine, rArray.size(), cElements); + va_list va; + va_start(va, cElements); + for (unsigned i = 0; i < cElements; i++) + { + int iExpected = va_arg(va, int); + if (rArray.at(i)->m_iValue != iExpected) + RTTestIFailed("line %u: element #%u: %d, expected %d", iLine, i, rArray.at(i)->m_iValue, iExpected); + } + va_end(va); +} + + +static void testArray() +{ + RTTestSub(g_hTest, "RTCRestArray"); + + { + RTCRestArray<RTCRestBool> obj1; + RTTESTI_CHECK(obj1.size() == 0); + RTTESTI_CHECK(obj1.isEmpty() == true); + RTTESTI_CHECK(obj1.isNull() == false); + RTTESTI_CHECK(strcmp(obj1.typeName(), "RTCRestArray<ElementType>") == 0); + RTTESTI_CHECK(obj1.typeClass() == RTCRestObjectBase::kTypeClass_Array); + } + + /* Some random order insertion and manipulations: */ + { + RTCRestArray<MyRestInt16> Arr2; + RTCRestArray<MyRestInt16> const *pConstArr2 = &Arr2; + + RTTESTI_CHECK_RC(Arr2.insert(0, new MyRestInt16(3)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 1, 3); + RTTESTI_CHECK_RC(Arr2.append( new MyRestInt16(7)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 2, 3, 7); + RTTESTI_CHECK_RC(Arr2.insert(1, new MyRestInt16(5)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 3, 3, 5, 7); + RTTESTI_CHECK_RC(Arr2.insert(2, new MyRestInt16(6)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 4, 3, 5, 6, 7); + RTTESTI_CHECK_RC(Arr2.prepend( new MyRestInt16(0)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 5, 0, 3, 5, 6, 7); + RTTESTI_CHECK_RC(Arr2.append( new MyRestInt16(9)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 6, 0, 3, 5, 6, 7, 9); + RTTESTI_CHECK_RC(Arr2.insert(5, new MyRestInt16(8)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 7, 0, 3, 5, 6, 7, 8, 9); + RTTESTI_CHECK_RC(Arr2.insert(1, new MyRestInt16(1)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 8, 0, 1, 3, 5, 6, 7, 8, 9); + RTTESTI_CHECK_RC(Arr2.insert(3, new MyRestInt16(4)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 9, 0, 1, 3, 4, 5, 6, 7, 8, 9); + RTTESTI_CHECK_RC(Arr2.insert(2, new MyRestInt16(2)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size(), ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size())); + + RTTESTI_CHECK(Arr2.size() == 10); + + for (size_t i = 0; i < Arr2.size(); i++) + { + MyRestInt16 *pCur = Arr2.at(i); + RTTESTI_CHECK(pCur->m_iValue == (int16_t)i); + + MyRestInt16 const *pCur2 = pConstArr2->at(i); + RTTESTI_CHECK(pCur2->m_iValue == (int16_t)i); + } + + RTTESTI_CHECK_RC(Arr2.replace(2, new MyRestInt16(22)), VWRN_ALREADY_EXISTS); + verifyArray(Arr2, __LINE__, 10, 0, 1, 22, 3, 4, 5, 6, 7, 8, 9); + + RTTESTI_CHECK_RC(Arr2.replace(7, new MyRestInt16(77)), VWRN_ALREADY_EXISTS); + verifyArray(Arr2, __LINE__, 10, 0, 1, 22, 3, 4, 5, 6, 77, 8, 9); + + RTTESTI_CHECK_RC(Arr2.replace(10, new MyRestInt16(10)), VINF_SUCCESS); + verifyArray(Arr2, __LINE__, 11, 0, 1, 22, 3, 4, 5, 6, 77, 8, 9, 10); + + RTTESTI_CHECK_RC(Arr2.replaceCopy(2, MyRestInt16(2)), VWRN_ALREADY_EXISTS); + verifyArray(Arr2, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size(), ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size())); + + /* copy constructor: */ + { + RTCRestArray<MyRestInt16> const Arr2Copy(Arr2); + verifyArray(Arr2Copy, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + } + verifyArray(Arr2, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size(), ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size())); + + { + RTCRestArray<MyRestInt16> Arr2Copy2(Arr2); + verifyArray(Arr2Copy2, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + RTTESTI_CHECK_RC(Arr2Copy2.removeAt(7), VINF_SUCCESS); + verifyArray(Arr2Copy2, __LINE__, 10, 0, 1, 2, 3, 4, 5, 6, 8, 9, 10); + } + verifyArray(Arr2, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size(), ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size())); + + /* copy method + clear: */ + { + RTCRestArray<MyRestInt16> Arr2Copy3; + RTTESTI_CHECK_RC(Arr2Copy3.assignCopy(Arr2), VINF_SUCCESS); + verifyArray(Arr2Copy3, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + Arr2Copy3.at(3)->m_iValue = 33; + verifyArray(Arr2Copy3, __LINE__, 11, 0, 1, 2, 33, 4, 5, 6, 77, 8, 9, 10); + Arr2Copy3.clear(); + verifyArray(Arr2Copy3, __LINE__, 0); + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size(), ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size())); + } + verifyArray(Arr2, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size(), ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size())); + + /* Check setNull and resetToDefaults with copies: */ + { + RTCRestArray<MyRestInt16> Arr2Copy4(Arr2); + verifyArray(Arr2Copy4, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + + RTTESTI_CHECK_RC(Arr2Copy4.setNull(), VINF_SUCCESS); + verifyArray(Arr2Copy4, __LINE__, 0); + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size(), ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size())); + RTTESTI_CHECK(Arr2Copy4.isNull() == true); + + RTTESTI_CHECK_RC(Arr2Copy4.resetToDefault(), VINF_SUCCESS); + RTTESTI_CHECK(Arr2Copy4.isNull() == false); + verifyArray(Arr2Copy4, __LINE__, 0); + } + verifyArray(Arr2, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + { + RTCRestArray<MyRestInt16> Arr2Copy5(Arr2); + verifyArray(Arr2Copy5, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + + RTTESTI_CHECK_RC(Arr2Copy5.resetToDefault(), VINF_SUCCESS); + verifyArray(Arr2Copy5, __LINE__, 0); + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size(), ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size())); + RTTESTI_CHECK(Arr2Copy5.isNull() == false); + + RTTESTI_CHECK_RC(Arr2Copy5.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(Arr2Copy5.isNull() == true); + + RTTESTI_CHECK_RC(Arr2Copy5.append(new MyRestInt16(100)), VINF_SUCCESS); + RTTESTI_CHECK(Arr2Copy5.isNull() == false); + verifyArray(Arr2Copy5, __LINE__, 1, 100); + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size() + 1, ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size() + 1)); + } + verifyArray(Arr2, __LINE__, 11, 0, 1, 2, 3, 4, 5, 6, 77, 8, 9, 10); + + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == Arr2.size(), ("%zu vs %zu\n", MyRestInt16::s_cInstances, Arr2.size())); + } + RTTESTI_CHECK_MSG(MyRestInt16::s_cInstances == 0, ("%zu\n", MyRestInt16::s_cInstances)); + + { + RTCRestArray<RTCRestInt64> Arr3; + RTCRestArray<RTCRestInt64> const *pConstArr3 = &Arr3; + + /* Insert a range of numbers into a int64 array. */ + for (int64_t i = 0; i < _64K; i++) + { + if (i & 1) + { + RTCRestInt64 toCopy(i); + if (i & 2) + RTTESTI_CHECK_RC(Arr3.insertCopy(i, toCopy), VINF_SUCCESS); + else + RTTESTI_CHECK_RC(Arr3.appendCopy(toCopy), VINF_SUCCESS); + } + else + { + RTCRestInt64 *pDirect = new RTCRestInt64(i); + if (i & 2) + RTTESTI_CHECK_RC(Arr3.insert(i, pDirect), VINF_SUCCESS); + else + RTTESTI_CHECK_RC(Arr3.append(pDirect), VINF_SUCCESS); + } + RTTESTI_CHECK(Arr3.size() == (size_t)i + 1); + RTTESTI_CHECK(Arr3.isEmpty() == false); + } + + /* Verify insertions: */ + size_t cElements = _64K; + RTTESTI_CHECK(Arr3.size() == cElements); + + for (int64_t i = 0; i < _64K; i++) + { + RTCRestInt64 *pCur = Arr3.at(i); + RTTESTI_CHECK(pCur->m_iValue == i); + + RTCRestInt64 const *pCur2 = pConstArr3->at(i); + RTTESTI_CHECK(pCur2->m_iValue == i); + } + RTTESTI_CHECK(Arr3.first()->m_iValue == 0); + RTTESTI_CHECK(Arr3.last()->m_iValue == _64K - 1); + RTTESTI_CHECK(pConstArr3->first()->m_iValue == 0); + RTTESTI_CHECK(pConstArr3->last()->m_iValue == _64K - 1); + + /* Remove every 3rd element: */ + RTTESTI_CHECK(Arr3.size() == cElements); + for (int64_t i = _64K - 1; i >= 0; i -= 3) + { + RTTESTI_CHECK_RC(Arr3.removeAt(i), VINF_SUCCESS); + cElements--; + RTTESTI_CHECK(Arr3.size() == cElements); + } + + /* Verify after removal: */ + for (int64_t i = 0, iValue = 0; i < (ssize_t)Arr3.size(); i++, iValue++) + { + if ((iValue % 3) == 0) + iValue++; + RTTESTI_CHECK_MSG(Arr3.at(i)->m_iValue == iValue, ("%RI64: %RI64 vs %RI64\n", i, Arr3.at(i)->m_iValue, iValue)); + } + + /* Clear it and we're done: */ + Arr3.clear(); + RTTESTI_CHECK(Arr3.size() == 0); + RTTESTI_CHECK(Arr3.isEmpty() == true); + } + + { + RTCRestArray<RTCRestInt32> Arr4; + + /* Insert a range of numbers into a int32 array, in reverse order. */ + for (int32_t i = 0; i < 2048; i++) + { + if (i & 1) + { + RTCRestInt32 toCopy(i); + if (i & 2) + RTTESTI_CHECK_RC(Arr4.insertCopy(0, toCopy), VINF_SUCCESS); + else + RTTESTI_CHECK_RC(Arr4.prependCopy(toCopy), VINF_SUCCESS); + } + else + { + RTCRestInt32 *pDirect = new RTCRestInt32(i); + if (i & 2) + RTTESTI_CHECK_RC(Arr4.insert(0, pDirect), VINF_SUCCESS); + else + RTTESTI_CHECK_RC(Arr4.prepend(pDirect), VINF_SUCCESS); + } + RTTESTI_CHECK((ssize_t)Arr4.size() == i + 1); + RTTESTI_CHECK(Arr4.isEmpty() == false); + } + + for (int32_t i = 0, iValue = (int32_t)Arr4.size() - 1; i < (ssize_t)Arr4.size(); i++, iValue--) + RTTESTI_CHECK_MSG(Arr4.at(i)->m_iValue == iValue, ("%RI32: %RI32 vs %RI32\n", i, Arr4.at(i)->m_iValue, iValue)); + + for (int32_t i = 0; i < 512; i++) + RTTESTI_CHECK_RC(Arr4.removeAt(0), VINF_SUCCESS); + RTTESTI_CHECK(Arr4.size() == 1536); + + for (int32_t i = 0; i < 512; i++) + RTTESTI_CHECK_RC(Arr4.removeAt(~(size_t)0), VINF_SUCCESS); + RTTESTI_CHECK(Arr4.size() == 1024); + + for (int32_t i = 0, iValue = 1535; i < (ssize_t)Arr4.size(); i++, iValue--) + RTTESTI_CHECK_MSG(Arr4.at(i)->m_iValue == iValue, ("%RI32: %RI32 vs %RI32\n", i, Arr4.at(i)->m_iValue, iValue)); + } +} + + +static void verifyMap(RTCRestStringMap<MyRestInt16> const &rMap, int iLine, unsigned cEntries, ...) +{ + if (rMap.size() != cEntries) + RTTestIFailed("line %u: size() -> %zu, expected %u", iLine, rMap.size(), cEntries); + if (rMap.isEmpty() != (cEntries ? false : true)) + RTTestIFailed("line %u: isEmpty() -> %RTbool, with %u entries", iLine, rMap.isEmpty(), cEntries); + + va_list va; + va_start(va, cEntries); + for (unsigned i = 0; i < cEntries; i++) + { + const char *pszKey = va_arg(va, const char *); + int iValue = va_arg(va, int); + if ( rMap.containsKey(pszKey) != true + || rMap.containsKey(RTCString(pszKey)) != true + || rMap.get(pszKey) == NULL + || rMap.get(RTCString(pszKey)) == NULL) + RTTestIFailed("line %u: entry '%s' not found!", iLine, pszKey); + else if (rMap.get(pszKey)->m_iValue != iValue) + RTTestIFailed("line %u: entry '%s' value mismatch: %d, expected %d", + iLine, pszKey, rMap.get(pszKey)->m_iValue, iValue); + RTTESTI_CHECK(rMap.get(pszKey) == rMap.get(RTCString(pszKey))); + } + va_end(va); + RTTESTI_CHECK(rMap.isNull() == false); + + uint64_t fFound = 0; + for (RTCRestStringMapBase::ConstIterator it = rMap.begin(); it != rMap.end(); ++it) + { + MyRestInt16 const *pObj = (MyRestInt16 const *)it.getValue(); + RTTESTI_CHECK(RT_VALID_PTR(pObj)); + + bool fFoundIt = false; + va_start(va, cEntries); + for (unsigned i = 0; i < cEntries; i++) + { + const char *pszKey = va_arg(va, const char *); + int iValue = va_arg(va, int); + if (it.getKey().equals(pszKey)) + { + if (fFound & RT_BIT_64(i)) + RTTestIFailed("line %u: base enum: entry '%s' returned more than once!", iLine, pszKey); + if (pObj->m_iValue != iValue) + RTTestIFailed("line %u: base enum: entry '%s' value mismatch: %d, expected %d", + iLine, pszKey, pObj->m_iValue, iValue); + fFound |= RT_BIT_64(i); + fFoundIt = true; + va_end(va); + return; + } + } + va_end(va); + if (!fFoundIt) + RTTestIFailed("line %u: base enum: entry '%s' not expected!", iLine, it.getKey().c_str()); + } +} + + +void testStringMap(void) +{ + RTTestSub(g_hTest, "RTCRestMap"); + + { + RTCRestStringMap<RTCRestString> obj1; + RTTESTI_CHECK(obj1.size() == 0); + RTTESTI_CHECK(obj1.isEmpty() == true); + RTTESTI_CHECK(obj1.isNull() == false); + RTTESTI_CHECK(strcmp(obj1.typeName(), "RTCRestStringMap<ValueType>") == 0); + RTTESTI_CHECK(obj1.typeClass() == RTCRestObjectBase::kTypeClass_StringMap); + } + + /* Basic operations: */ + { + MyRestInt16::s_cInstances = 0; + RTCRestStringMap<MyRestInt16> Map2; + verifyMap(Map2, __LINE__, 0); + + RTTESTI_CHECK_RC(Map2.putCopy("0x0004", MyRestInt16(4)), VINF_SUCCESS); + verifyMap(Map2, __LINE__, 1, "0x0004", 4); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 1); + RTTESTI_CHECK_RC(Map2.put("0x0001", new MyRestInt16(1)), VINF_SUCCESS); + verifyMap(Map2, __LINE__, 2, "0x0004",4, "0x0001",1); + RTTESTI_CHECK_RC(Map2.put("0x0003", new MyRestInt16(3)), VINF_SUCCESS); + verifyMap(Map2, __LINE__, 3, "0x0004",4, "0x0001",1, "0x0003",3); + RTTESTI_CHECK_RC(Map2.put("0x0002", new MyRestInt16(2)), VINF_SUCCESS); + verifyMap(Map2, __LINE__, 4, "0x0004",4, "0x0001",1, "0x0003",3, "0x0002",2); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 4); + RTTESTI_CHECK_RC(Map2.put("0x0000", new MyRestInt16(0)), VINF_SUCCESS); + verifyMap(Map2, __LINE__, 5, "0x0004",4, "0x0001",1, "0x0003",3, "0x0002",2, "0x0000",0); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 5); + RTTESTI_CHECK_RC(Map2.putCopy("towel", MyRestInt16(42)), VINF_SUCCESS); + verifyMap(Map2, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0003",3, "0x0002",2, "0x0000",0, "towel",42); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 6); + + RTTESTI_CHECK(Map2.containsKey("0x0005") == false); + RTTESTI_CHECK(Map2.get("0x0005") == NULL); + + RTTESTI_CHECK(Map2.remove("0x0003") == true); + verifyMap(Map2, __LINE__, 5, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 5); + + RTTESTI_CHECK(Map2.remove("0x0003") == false); + verifyMap(Map2, __LINE__, 5, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 5); + + RTCRestObjectBase *pNewBase = NULL; + RTTESTI_CHECK_RC(Map2.putNewValue(&pNewBase, "putNewValue"), VINF_SUCCESS); + ((MyRestInt16 *)pNewBase)->m_iValue = 88; + RTTESTI_CHECK(MyRestInt16::s_cInstances == 6); + verifyMap(Map2, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",88); + + pNewBase = NULL; + RTTESTI_CHECK_RC(Map2.putNewValue(&pNewBase, RTCString("putNewValue")), VERR_ALREADY_EXISTS); + verifyMap(Map2, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",88); + pNewBase = NULL; + RTTESTI_CHECK_RC(Map2.putNewValue(&pNewBase, RTCString("putNewValue"), true /*a_fReplace*/), VWRN_ALREADY_EXISTS); + verifyMap(Map2, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 6); + + /* Make copy and remove all: */ + { + RTCRestStringMap<MyRestInt16> Map2Copy1; + + RTTESTI_CHECK_RC(Map2Copy1.assignCopy(Map2), VINF_SUCCESS); + verifyMap(Map2Copy1, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 12); + + RTTESTI_CHECK(Map2Copy1.remove("0x0004") == true); + verifyMap(Map2Copy1, __LINE__, 5, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 11); + + RTTESTI_CHECK(Map2Copy1.remove("putNewValue") == true); + verifyMap(Map2Copy1, __LINE__, 4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 10); + + RTTESTI_CHECK(Map2Copy1.remove("towel") == true); + verifyMap(Map2Copy1, __LINE__, 3, "0x0001",1, "0x0002",2, "0x0000",0); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 9); + + RTTESTI_CHECK(Map2Copy1.remove("0x0002") == true); + verifyMap(Map2Copy1, __LINE__, 2, "0x0001",1, "0x0000",0); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 8); + + RTTESTI_CHECK(Map2Copy1.remove("0x0000") == true); + verifyMap(Map2Copy1, __LINE__, 1, "0x0001",1); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 7); + + RTTESTI_CHECK(Map2Copy1.remove("0x0001") == true); + verifyMap(Map2Copy1, __LINE__, 0); + RTTESTI_CHECK(Map2Copy1.isEmpty() == true); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 6); + } + verifyMap(Map2, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + + /* Make copy and use clear: */ + { + RTCRestStringMap<MyRestInt16> Map2Copy2(Map2); + verifyMap(Map2Copy2, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 12); + Map2Copy2.clear(); + verifyMap(Map2Copy2, __LINE__, 0); + RTTESTI_CHECK(Map2Copy2.isEmpty() == true); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 6); + } + verifyMap(Map2, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + + /* Make copy and reset to default: */ + { + RTCRestStringMap<MyRestInt16> Map2Copy3(Map2); + verifyMap(Map2Copy3, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 12); + RTTESTI_CHECK_RC(Map2Copy3.resetToDefault(), VINF_SUCCESS); + verifyMap(Map2Copy3, __LINE__, 0); + RTTESTI_CHECK(Map2Copy3.isEmpty() == true); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 6); + } + verifyMap(Map2, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + + /* Make copy and set to null: */ + { + RTCRestStringMap<MyRestInt16> Map2Copy4; + Map2Copy4 = Map2; + verifyMap(Map2Copy4, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 12); + RTTESTI_CHECK_RC(Map2Copy4.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(Map2Copy4.size() == 0); + RTTESTI_CHECK(Map2Copy4.isEmpty() == true); + RTTESTI_CHECK(Map2Copy4.isNull() == true); + RTTESTI_CHECK(MyRestInt16::s_cInstances == 6); + } + verifyMap(Map2, __LINE__, 6, "0x0004",4, "0x0001",1, "0x0002",2, "0x0000",0, "towel",42, "putNewValue",0); + } + RTTESTI_CHECK(MyRestInt16::s_cInstances == 0); + + /* Check that null indicator is reset when it should: */ + { + RTCRestStringMap<MyRestInt16> Map3; + Map3.setNull(); + RTTESTI_CHECK_RC(Map3.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(Map3.size() == 0); + RTTESTI_CHECK(Map3.isEmpty() == true); + RTTESTI_CHECK(Map3.isNull() == true); + RTTESTI_CHECK_RC(Map3.putCopy("not-null-anymore", MyRestInt16(1)), VINF_SUCCESS); + verifyMap(Map3, __LINE__, 1, "not-null-anymore",1); + } + RTTESTI_CHECK(MyRestInt16::s_cInstances == 0); + + { + RTCRestStringMap<MyRestInt16> Map4; + Map4.setNull(); + RTTESTI_CHECK_RC(Map4.setNull(), VINF_SUCCESS); + RTTESTI_CHECK(Map4.size() == 0); + RTTESTI_CHECK(Map4.isEmpty() == true); + RTTESTI_CHECK(Map4.isNull() == true); + RTCRestObjectBase *pNewBase = NULL; + RTTESTI_CHECK_RC(Map4.putNewValue(&pNewBase, "putNewValue"), VINF_SUCCESS); + verifyMap(Map4, __LINE__, 1, "putNewValue",0); + } + RTTESTI_CHECK(MyRestInt16::s_cInstances == 0); +} + + +class TestRequest : public RTCRestClientRequestBase +{ +public: + RTCRestString m_strValue; + RTCRestInt64 m_iValue; + RTCRestArray<RTCRestString> m_Array; + RTCRestStringMap<RTCRestString> m_Map; + /** @todo add more attributes. */ + + TestRequest(const char *a_pszValue, int64_t a_iValue, unsigned a_cElements, ...) + : RTCRestClientRequestBase() + , m_strValue(a_pszValue) + , m_iValue(a_iValue) + { + m_fIsSet = UINT64_MAX; + va_list va; + va_start(va, a_cElements); + for (unsigned i = 0; i < a_cElements; i++) + m_Array.append(new RTCRestString(va_arg(va, const char *))); + va_end(va); + } + + int resetToDefault() RT_NOEXCEPT RT_OVERRIDE + { + m_strValue = ""; + m_iValue = 0; + return m_Array.resetToDefault(); + } + + int xmitPrepare(RTCString *a_pStrPath, RTCString *a_pStrQuery, RTHTTP a_hHttp, RTCString *a_pStrBody) const RT_NOEXCEPT RT_OVERRIDE + { + RT_NOREF(a_pStrPath, a_pStrQuery, a_hHttp, a_pStrBody); + return VINF_SUCCESS; + } + + void xmitComplete(int a_rcStatus, RTHTTP a_hHttp) const RT_NOEXCEPT RT_OVERRIDE + { + RT_NOREF(a_rcStatus, a_hHttp); + } + + void testPath(const char *a_pszExpected) + { + static PATHPARAMDESC const s_aParams[] = + { + { RT_STR_TUPLE("{string}"), 0, 0 }, + { RT_STR_TUPLE("{integer}"), 0, 0 }, + { RT_STR_TUPLE("{array}"), 0, 0 }, + }; + PATHPARAMSTATE aState[] = { { &m_strValue, 0 }, { &m_iValue, 0 }, { &m_Array, 0 } }; + RTCString strPath; + RTTESTI_CHECK_RC(doPathParameters(&strPath, RT_STR_TUPLE("my/{integer}/{string}/array:{array}/path"), + s_aParams, aState, RT_ELEMENTS(aState)), VINF_SUCCESS); + RTTESTI_CHECK_MSG(strPath.equals(a_pszExpected), ("actual: '%s'\nexpect: '%s'\n", strPath.c_str(), a_pszExpected)); + } + + void testQuery(const char *a_pszCsv, + const char *a_pszSsv, + const char *a_pszTsv, + const char *a_pszPipes, + const char *a_pszMulti) + { + QUERYPARAMDESC aParams[] = + { + { "string", 0, true, 0 }, + { "integer", 0, true, 0 }, + { "array", 0, true, 0 }, + }; + + RTCRestObjectBase const *apObjects[] = { &m_strValue, &m_iValue, &m_Array }; + RTCString strQuery; + RTTESTI_CHECK_RC(doQueryParameters(&strQuery, aParams, apObjects, RT_ELEMENTS(apObjects)), VINF_SUCCESS); + RTTESTI_CHECK_MSG(strQuery.equals(a_pszCsv), ("actual: '%s'\nexpect: %s\n", strQuery.c_str(), a_pszCsv)); + + strQuery.setNull(); + aParams[2].fFlags = RTCRestObjectBase::kCollectionFormat_csv; + RTTESTI_CHECK_RC(doQueryParameters(&strQuery, aParams, apObjects, RT_ELEMENTS(apObjects)), VINF_SUCCESS); + RTTESTI_CHECK_MSG(strQuery.equals(a_pszCsv), ("actual: '%s'\nexpect: %s\n", strQuery.c_str(), a_pszCsv)); + + strQuery.setNull(); + aParams[2].fFlags = RTCRestObjectBase::kCollectionFormat_ssv; + RTTESTI_CHECK_RC(doQueryParameters(&strQuery, aParams, apObjects, RT_ELEMENTS(apObjects)), VINF_SUCCESS); + RTTESTI_CHECK_MSG(strQuery.equals(a_pszSsv), ("actual: '%s'\nexpect: %s\n", strQuery.c_str(), a_pszSsv)); + + strQuery.setNull(); + aParams[2].fFlags = RTCRestObjectBase::kCollectionFormat_tsv; + RTTESTI_CHECK_RC(doQueryParameters(&strQuery, aParams, apObjects, RT_ELEMENTS(apObjects)), VINF_SUCCESS); + RTTESTI_CHECK_MSG(strQuery.equals(a_pszTsv), ("actual: '%s'\nexpect: %s\n", strQuery.c_str(), a_pszTsv)); + + strQuery.setNull(); + aParams[2].fFlags = RTCRestObjectBase::kCollectionFormat_pipes; + RTTESTI_CHECK_RC(doQueryParameters(&strQuery, aParams, apObjects, RT_ELEMENTS(apObjects)), VINF_SUCCESS); + RTTESTI_CHECK_MSG(strQuery.equals(a_pszPipes), ("actual: '%s'\nexpect: %s\n", strQuery.c_str(), a_pszPipes)); + + strQuery.setNull(); + aParams[2].fFlags = RTCRestObjectBase::kCollectionFormat_multi; + RTTESTI_CHECK_RC(doQueryParameters(&strQuery, aParams, apObjects, RT_ELEMENTS(apObjects)), VINF_SUCCESS); + RTTESTI_CHECK_MSG(strQuery.equals(a_pszMulti), ("actual: '%s'\nexpect: %s\n", strQuery.c_str(), a_pszMulti)); + } + + void testHeader(unsigned a_cHeaders, ...) + { + HEADERPARAMDESC aParams[] = + { + { "x-string", 0, true, 0, false }, + { "x-integer", 0, true, 0, false }, + { "x-array", 0, true, 0, false }, + { "x-map-", 0, true, 0, true }, + }; + RTCRestObjectBase const *apObjects[] = { &m_strValue, &m_iValue, &m_Array, &m_Map }; + RTHTTP hHttp = NIL_RTHTTP; + RTTESTI_CHECK_RC(RTHttpCreate(&hHttp), VINF_SUCCESS); + RTTESTI_CHECK_RC(doHeaderParameters(hHttp, aParams, apObjects, RT_ELEMENTS(apObjects)), VINF_SUCCESS); + RTTESTI_CHECK(RTHttpGetHeaderCount(hHttp) == a_cHeaders); + va_list va; + va_start(va, a_cHeaders); + for (size_t i = 0; i < a_cHeaders; i++) + { + const char *pszField = va_arg(va, const char *); + const char *pszValue = va_arg(va, const char *); + const char *pszActual = RTHttpGetHeader(hHttp, pszField, RTSTR_MAX); + RTTESTI_CHECK_MSG(RTStrCmp(pszActual, pszValue) == 0, + ("Header '%s' value is '%s' rather than '%s'", pszField, pszActual, pszValue)); + } + va_end(va); + RTTESTI_CHECK_RC(RTHttpDestroy(hHttp), VINF_SUCCESS); + } +}; + + +void testClientRequestBase() +{ + RTTestSub(g_hTest, "RTCRestClientRequestBase"); + { + TestRequest Req1("this-is-a-string", 123456789, 5, "1", "22", "333", "444", "555"); + Req1.testPath("my/123456789/this-is-a-string/array:1%2C22%2C333%2C444%2C555/path"); + Req1.testQuery("?string=this-is-a-string&integer=123456789&array=1%2C22%2C333%2C444%2C555", + "?string=this-is-a-string&integer=123456789&array=1%2022%20333%20444%20555", + "?string=this-is-a-string&integer=123456789&array=1%0922%09333%09444%09555", + "?string=this-is-a-string&integer=123456789&array=1%7C22%7C333%7C444%7C555", + "?string=this-is-a-string&integer=123456789&array=1&array=22&array=333&array=444&array=555"); + Req1.testHeader(3, "x-string","this-is-a-string", "x-integer","123456789", "x-array","1,22,333,444,555"); + } + { + TestRequest Req2(";'[]", 42, 3, "null", "foo", "bar"); + RTTESTI_CHECK_RC(Req2.m_Map.put("stuff-1", new RTCRestString("stuffy-value-1")), VINF_SUCCESS); + RTTESTI_CHECK_RC(Req2.m_Map.put("stuff-2", new RTCRestString("stuffy-value-2")), VINF_SUCCESS); + RTTESTI_CHECK_RC(Req2.m_Map.put("2222", new RTCRestString("33")), VINF_SUCCESS); + Req2.testPath("my/42/%3B%27%5B%5D/array:null%2Cfoo%2Cbar/path"); + Req2.testQuery("?string=%3B%27%5B%5D&integer=42&array=null%2Cfoo%2Cbar", + "?string=%3B%27%5B%5D&integer=42&array=null%20foo%20bar", + "?string=%3B%27%5B%5D&integer=42&array=null%09foo%09bar", + "?string=%3B%27%5B%5D&integer=42&array=null%7Cfoo%7Cbar", + "?string=%3B%27%5B%5D&integer=42&array=null&array=foo&array=bar"); + Req2.testHeader(6, "x-string",";'[]", "x-integer","42", "x-array","null,foo,bar", + "x-map-stuff-1","stuffy-value-1", + "x-map-stuff-2","stuffy-value-2", + "x-map-2222","33"); + } +} + + +class TestResponse : public RTCRestClientResponseBase +{ +public: + RTCRestArray<RTCRestString> *m_pArray; + RTCRestStringMap<RTCRestString> *m_pMap; + RTCRestInt64 *m_pInteger; + RTCRestString *m_pStrContentType; + + TestResponse() : m_pArray(NULL), m_pMap(NULL), m_pInteger(NULL), m_pStrContentType(NULL) + { } + +protected: + virtual int consumeHeader(uint32_t a_uMatchWord, const char *a_pchField, size_t a_cchField, + const char *a_pchValue, size_t a_cchValue) RT_NOEXCEPT RT_OVERRIDE + { + int rc = RTCRestClientResponseBase::consumeHeader(a_uMatchWord, a_pchField, a_cchField, a_pchValue, a_cchValue); + AssertRCReturn(rc, rc); + +#define MATCH_FIELD(a_sz) (sizeof(a_sz) - 1 == a_cchField && RTStrNICmpAscii(a_pchField, RT_STR_TUPLE(a_sz)) == 0) + if (MATCH_FIELD("x-array")) + { + if (!m_pArray) + { + m_pArray = new (std::nothrow) RTCRestArray<RTCRestString>(); + AssertReturn(m_pArray, VERR_NO_MEMORY); + return deserializeHeader(m_pArray, a_pchValue, a_cchValue, RTCRestObjectBase::kCollectionFormat_csv, "x-array"); + } + } + else if (a_cchField >= sizeof("x-map-") - 1 && RTStrNICmpAscii(a_pchField, RT_STR_TUPLE("x-map-")) == 0) + { + if (!m_pMap) + { + m_pMap = new (std::nothrow) RTCRestStringMap<RTCRestString>(); + AssertReturn(m_pMap, VERR_NO_MEMORY); + } + return deserializeHeaderIntoMap(m_pMap, a_pchField + 6, a_cchField - 6, a_pchValue, a_cchValue, 0, "x-map-"); + } + else if (MATCH_FIELD("x-integer")) + { + if (!m_pInteger) + { + m_pInteger = new (std::nothrow) RTCRestInt64(); + AssertReturn(m_pInteger, VERR_NO_MEMORY); + return deserializeHeader(m_pInteger, a_pchValue, a_cchValue, 0, "x-integer"); + } + } + else if (MATCH_FIELD("content-type")) + { + if (!m_pStrContentType) + { + m_pStrContentType = new (std::nothrow) RTCRestString(); + AssertReturn(m_pStrContentType, VERR_NO_MEMORY); + return deserializeHeader(m_pStrContentType, a_pchValue, a_cchValue, 0, "content-type"); + } + } + else + return VWRN_NOT_FOUND; + RT_NOREF(a_uMatchWord); + return addError(VERR_ALREADY_EXISTS, "Already have field '%.*s'!", a_cchField, a_pchField); + } + +public: + int pushHeader(const char *pszField, const char *pszValue) + { + size_t const cchField = strlen(pszField); + void *pvFieldCopy = RTTestGuardedAllocTail(g_hTest, cchField); + RTTESTI_CHECK_RET(pvFieldCopy, VERR_NO_MEMORY); + memcpy(pvFieldCopy, pszField, cchField); + + size_t const cchValue = strlen(pszValue); + void *pvValueCopy = RTTestGuardedAllocTail(g_hTest, cchValue); + RTTESTI_CHECK_RET(pvValueCopy, VERR_NO_MEMORY); + memcpy(pvValueCopy, pszValue, cchValue); + + uint32_t uWord = RTHTTP_MAKE_HDR_MATCH_WORD(cchField, + cchField >= 1 ? RT_C_TO_LOWER(pszField[0]) : 0, + cchField >= 2 ? RT_C_TO_LOWER(pszField[1]) : 0, + cchField >= 3 ? RT_C_TO_LOWER(pszField[2]) : 0); + int rc = consumeHeader(uWord, (const char *)pvFieldCopy, cchField, (const char *)pvValueCopy, cchValue); + RTTestGuardedFree(g_hTest, pvValueCopy); + RTTestGuardedFree(g_hTest, pvFieldCopy); + return rc; + } +}; + + +void testClientResponseBase() +{ + RTTestSub(g_hTest, "RTCRestClientResponseBase"); + { + TestResponse Resp1; + RTTESTI_CHECK_RC(Resp1.pushHeader("content-type", "application/json; charset=utf-8"), VINF_SUCCESS); + RTTESTI_CHECK(Resp1.getContentType().equals("application/json; charset=utf-8")); + RTTESTI_CHECK(Resp1.m_pStrContentType && Resp1.m_pStrContentType->equals("application/json; charset=utf-8")); + + RTTESTI_CHECK_RC(Resp1.pushHeader("content-typ2", "oopsy daisy"), VWRN_NOT_FOUND); + RTTESTI_CHECK_RC(Resp1.pushHeader("content-type2", "oopsy daisy"), VWRN_NOT_FOUND); + RTTESTI_CHECK(Resp1.getContentType().equals("application/json; charset=utf-8")); + RTTESTI_CHECK(Resp1.m_pStrContentType && Resp1.m_pStrContentType->equals("application/json; charset=utf-8")); + + RTTESTI_CHECK_RC(Resp1.pushHeader("x-integer", "398679406"), VINF_SUCCESS); + RTTESTI_CHECK(Resp1.m_pInteger && Resp1.m_pInteger->m_iValue == 398679406); + + RTTESTI_CHECK_RC(Resp1.pushHeader("x-array", "zero,one,two,three"), VINF_SUCCESS); + RTTESTI_CHECK(Resp1.m_pArray && Resp1.m_pArray->size() == 4); + + RTTESTI_CHECK_RC(Resp1.pushHeader("x-map-", "empty-key"), VINF_SUCCESS); + RTTESTI_CHECK(Resp1.m_pMap && Resp1.m_pMap->size() == 1 && Resp1.m_pMap->get("") != NULL && Resp1.m_pMap->get("")->equals("empty-key")); + + RTTESTI_CHECK_RC(Resp1.pushHeader("x-map-42", "key-is-42"), VINF_SUCCESS); + RTTESTI_CHECK(Resp1.m_pMap && Resp1.m_pMap->size() == 2 && Resp1.m_pMap->get("42") != NULL && Resp1.m_pMap->get("42")->equals("key-is-42")); + } +} + + +int main() +{ + RTEXITCODE rcExit = RTTestInitAndCreate("tstRTCRest-1", &g_hTest); + if (rcExit == RTEXITCODE_SUCCESS ) + { + testBool(); + testInteger<RTCRestInt64, int64_t, Int64Constants>(); + testInteger<RTCRestInt32, int32_t, Int32Constants>(); + testInteger<RTCRestInt16, int16_t, Int16Constants>(); + testDouble(); + testString("dummy", 1, 2); + testDate(); + testArray(); + testStringMap(); + testClientRequestBase(); + testClientResponseBase(); + + rcExit = RTTestSummaryAndDestroy(g_hTest); + } + return rcExit; +} + |