diff --git a/doc/xml/release.xml b/doc/xml/release.xml index b9b22f119..cd9e4fc6e 100644 --- a/doc/xml/release.xml +++ b/doc/xml/release.xml @@ -80,6 +80,10 @@

Add Buffer object.

+ + +

Add Variant and VariantList objects.

+
diff --git a/src/Makefile b/src/Makefile index 8a0ebdbaa..c975aeb5c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -10,6 +10,8 @@ pgbackrest: \ common/type/list.o \ common/type/string.o \ common/type/stringList.o \ + common/type/variant.o \ + common/type/variantList.o \ config/config.o \ config/define.o \ config/parse.o \ @@ -23,6 +25,8 @@ pgbackrest: \ common/type/list.o \ common/type/string.o \ common/type/stringList.o \ + common/type/variant.o \ + common/type/variantList.o \ config/config.o \ config/define.o \ config/parse.o \ diff --git a/src/common/type/stringList.c b/src/common/type/stringList.c index 2c4a6518d..fe4af4c3c 100644 --- a/src/common/type/stringList.c +++ b/src/common/type/stringList.c @@ -53,6 +53,22 @@ strLstNewSplit(const String *string, const String *delimiter) return this; } +/*********************************************************************************************************************************** +New string list from a variant list -- all variants in the list must be type string +***********************************************************************************************************************************/ +StringList * +strLstNewVarLst(const VariantList *sourceList) +{ + // Create the list + StringList *this = strLstNew(); + + // Copy variants + for (unsigned int listIdx = 0; listIdx < varLstSize(sourceList); listIdx++) + strLstAdd(this, varStr(varLstGet(sourceList, listIdx))); + + return this; +} + /*********************************************************************************************************************************** Duplicate a string list ***********************************************************************************************************************************/ diff --git a/src/common/type/stringList.h b/src/common/type/stringList.h index d1e267d16..b04825ec6 100644 --- a/src/common/type/stringList.h +++ b/src/common/type/stringList.h @@ -14,8 +14,11 @@ typedef struct StringList StringList; /*********************************************************************************************************************************** Functions ***********************************************************************************************************************************/ +#include "common/type/variantList.h" + StringList *strLstNew(); StringList *strLstNewSplit(const String *string, const String *delimiter); +StringList *strLstNewVarLst(const VariantList *sourceList); StringList *strLstDup(const StringList *sourceList); StringList *strLstAdd(StringList *this, String *string); diff --git a/src/common/type/variant.c b/src/common/type/variant.c new file mode 100644 index 000000000..7a2dfefd2 --- /dev/null +++ b/src/common/type/variant.c @@ -0,0 +1,507 @@ +/*********************************************************************************************************************************** +Variant Data Type +***********************************************************************************************************************************/ +#include +#include +#include + +#include "common/errorType.h" +#include "common/memContext.h" +#include "common/type/variant.h" + +/*********************************************************************************************************************************** +Information about the variant +***********************************************************************************************************************************/ +struct Variant +{ + MemContext *memContext; // Mem context + unsigned int type:3; // Variant Type +}; + +/*********************************************************************************************************************************** +Variant type names +***********************************************************************************************************************************/ +static const char *variantTypeName[] = +{ + "bool", // varTypeBool + "double", // varTypeDouble, + "int", // varTypeInt + "KeyValue", // varTypeKeyValue + "String", // varTypeString + "VariantList", // varTypeVariantList +}; + +/*********************************************************************************************************************************** +New variant of any supported type +***********************************************************************************************************************************/ +static Variant * +varNewInternal(VariantType type, void *data, size_t dataSize) +{ + // Allocate memory for the variant and set the type + Variant *this = memNew(sizeof(Variant) + dataSize); + this->memContext = memContextCurrent(); + this->type = type; + + // Copy data if there is any + if (dataSize > 0) + memcpy((unsigned char *)this + sizeof(Variant), data, dataSize); + + return this; +} + +/*********************************************************************************************************************************** +Get a pointer to the data stored in the variant. This hides the complicated pointer arithmetic. +***********************************************************************************************************************************/ +static void * +varData(const Variant *this) +{ + return (void *)((unsigned char *)this + sizeof(Variant)); +} + +/*********************************************************************************************************************************** +Duplicate a variant +***********************************************************************************************************************************/ +Variant * +varDup(const Variant *this) +{ + Variant *result = NULL; + + if (this != NULL) + { + switch (this->type) + { + case varTypeBool: + { + result = varNewBool(varBool(this)); + break; + } + + case varTypeDouble: + { + result = varNewDbl(varDbl(this)); + break; + } + + case varTypeInt: + { + result = varNewInt(varInt(this)); + break; + } + + case varTypeString: + { + result = varNewStr(varStr(this)); + break; + } + + case varTypeVariantList: + { + result = varNewVarLst(varVarLst(this)); + break; + } + } + } + + return result; +} + +/*********************************************************************************************************************************** +Test if variants are equal +***********************************************************************************************************************************/ +bool +varEq(const Variant *this1, const Variant *this2) +{ + bool result = false; + + // Test if both variants are non-null + if (this1 != NULL && this2 != NULL) + { + // Test if both variants are of the same type + if (varType(this1) == varType(this2)) + { + switch (varType(this1)) + { + case varTypeBool: + { + result = varBool(this1) == varBool(this2); + break; + } + + case varTypeDouble: + { + result = varDbl(this1) == varDbl(this2); + break; + } + + case varTypeInt: + { + result = varInt(this1) == varInt(this2); + break; + } + + case varTypeString: + { + result = strEq(varStr(this1), varStr(this2)); + break; + } + + default: + THROW(AssertError, "unable to test equality for %s", variantTypeName[this1->type]); + } + } + } + // Else they are equal if they are both null + else + result = this1 == NULL && this2 == NULL; + + return result; +} + +/*********************************************************************************************************************************** +Get variant type +***********************************************************************************************************************************/ +VariantType +varType(const Variant *this) +{ + return this->type; +} + +/*********************************************************************************************************************************** +New bool variant +***********************************************************************************************************************************/ +Variant * +varNewBool(bool data) +{ + return varNewInternal(varTypeBool, (void *)&data, sizeof(data)); +} + +/*********************************************************************************************************************************** +Return bool +***********************************************************************************************************************************/ +bool +varBool(const Variant *this) +{ + // Only valid for int + if (this->type != varTypeBool) + THROW(AssertError, "variant type is not bool"); + + // Get the int + return *((bool *)varData(this)); +} + +/*********************************************************************************************************************************** +Return bool regardless of variant type +***********************************************************************************************************************************/ +bool +varBoolForce(const Variant *this) +{ + int result = 0; + + switch (this->type) + { + case varTypeBool: + result = varBool(this); + break; + + case varTypeInt: + result = varInt(this) != 0; + break; + + case varTypeString: + { + // List of false/true boolean string values. Note that false/true values must be equal. + static const char *boolString[] = + { + "n", "f", "0", "no", "false", "off", + "y", "t", "1", "yes", "true", "on", + }; + + // Search for the string + const char *string = strPtr(varStr(this)); + unsigned int boolIdx; + + for (boolIdx = 0; boolIdx < sizeof(boolString) / sizeof(char *); boolIdx++) + if (strcasecmp(string, boolString[boolIdx]) == 0) + break; + + // If string was not found then not a boolean + if (boolIdx == sizeof(boolString) / sizeof(char *)) + THROW(FormatError, "unable to convert str '%s' to bool", string); + + // False if in first half of list, true if in second half + result = boolIdx / (sizeof(boolString) / sizeof(char *) / 2); + + break; + } + + default: + THROW(FormatError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeBool]); + } + + return result; +} + +/*********************************************************************************************************************************** +New double variant +***********************************************************************************************************************************/ +Variant * +varNewDbl(double data) +{ + return varNewInternal(varTypeDouble, (unsigned char *)&data, sizeof(data)); +} + +/*********************************************************************************************************************************** +Return double +***********************************************************************************************************************************/ +double +varDbl(const Variant *this) +{ + // Only valid for double + if (this->type != varTypeDouble) + THROW(AssertError, "variant type is not double"); + + // Get the int + return *((double *)varData(this)); +} + +/*********************************************************************************************************************************** +Return double regardless of variant type +***********************************************************************************************************************************/ +double +varDblForce(const Variant *this) +{ + double result = 0; + + switch (this->type) + { + case varTypeBool: + { + result = varBool(this); + break; + } + + case varTypeDouble: + { + result = varDbl(this); + break; + } + + case varTypeInt: + { + result = varInt(this); + break; + } + + case varTypeString: + { + sscanf(strPtr(varStr(this)), "%lf", &result); + + if (result == 0 && strcmp(strPtr(varStr(this)), "0") != 0) + { + THROW( + FormatError, "unable to force %s '%s' to %s", variantTypeName[this->type], strPtr(varStr(this)), + variantTypeName[varTypeDouble]); + } + + break; + } + + default: + THROW(FormatError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeDouble]); + } + + return result; +} + +/*********************************************************************************************************************************** +New int variant +***********************************************************************************************************************************/ +Variant * +varNewInt(int data) +{ + return varNewInternal(varTypeInt, (void *)&data, sizeof(data)); +} + +/*********************************************************************************************************************************** +Return int +***********************************************************************************************************************************/ +int +varInt(const Variant *this) +{ + // Only valid for int + if (this->type != varTypeInt) + THROW(AssertError, "variant type is not int"); + + // Get the int + return *((int *)varData(this)); +} + +/*********************************************************************************************************************************** +Return int regardless of variant type +***********************************************************************************************************************************/ +int +varIntForce(const Variant *this) +{ + int result = 0; + + switch (this->type) + { + case varTypeBool: + { + result = varBool(this); + break; + } + + case varTypeInt: + { + result = varInt(this); + break; + } + + case varTypeString: + { + result = atoi(strPtr(varStr(this))); + + if (result == 0 && strcmp(strPtr(varStr(this)), "0") != 0) + THROW(FormatError, "unable to convert str '%s' to int", strPtr(varStr(this))); + + break; + } + + default: + THROW(FormatError, "unable to force %s to %s", variantTypeName[this->type], variantTypeName[varTypeInt]); + } + + return result; +} + +/*********************************************************************************************************************************** +New string variant +***********************************************************************************************************************************/ +Variant * +varNewStr(const String *data) +{ + // Make sure the string is not NULL + if (data == NULL) + THROW(AssertError, "string variant cannot be NULL"); + + // Create the variant + String *dataCopy = strDup(data); + return varNewInternal(varTypeString, (void *)&dataCopy, sizeof(dataCopy)); +} + +/*********************************************************************************************************************************** +New string variant from a zero-terminated string +***********************************************************************************************************************************/ +Variant * +varNewStrZ(const char *data) +{ + // Make sure the string is not NULL + if (data == NULL) + THROW(AssertError, "zero-terminated string cannot be NULL"); + + // Create the variant + String *dataCopy = strNew(data); + return varNewInternal(varTypeString, (void *)&dataCopy, sizeof(dataCopy)); +} + +/*********************************************************************************************************************************** +Return string +***********************************************************************************************************************************/ +String * +varStr(const Variant *this) +{ + String *result = NULL; + + if (this != NULL) + { + // Only valid for strings + if (this->type != varTypeString) + THROW(AssertError, "variant type is not string"); + + // Get the string + result = *((String **)varData(this)); + } + + return result; +} + +/*********************************************************************************************************************************** +New variant list variant +***********************************************************************************************************************************/ +Variant * +varNewVarLst(const VariantList *data) +{ + // Create the variant + VariantList *dataCopy = varLstDup(data); + return varNewInternal(varTypeVariantList, (void *)&dataCopy, sizeof(dataCopy)); +} + +/*********************************************************************************************************************************** +New empty variant list variant +***********************************************************************************************************************************/ +Variant * +varNewVarLstEmpty() +{ + // Create the variant + VariantList *data = varLstNew(); + return varNewInternal(varTypeVariantList, (void *)&data, sizeof(data)); +} + +/*********************************************************************************************************************************** +Return key/value +***********************************************************************************************************************************/ +VariantList * +varVarLst(const Variant *this) +{ + VariantList *result = NULL; + + if (this != NULL) + { + // Only valid for key/value + if (this->type != varTypeVariantList) + THROW(AssertError, "variant type is not '%s'", variantTypeName[varTypeVariantList]); + + // Get the string + result = *((VariantList **)varData(this)); + } + + return result; +} + +/*********************************************************************************************************************************** +Free variant +***********************************************************************************************************************************/ +void +varFree(Variant *this) +{ + if (this != NULL) + { + MEM_CONTEXT_BEGIN(this->memContext) + { + switch (this->type) + { + case varTypeString: + { + strFree(varStr(this)); + break; + } + + case varTypeVariantList: + { + varLstFree(varVarLst(this)); + break; + } + + // Nothing additional to free for these types + case varTypeBool: + case varTypeDouble: + case varTypeInt: + break; + } + + memFree(this); + } + MEM_CONTEXT_END(); + } +} diff --git a/src/common/type/variant.h b/src/common/type/variant.h new file mode 100644 index 000000000..de4252d14 --- /dev/null +++ b/src/common/type/variant.h @@ -0,0 +1,59 @@ +/*********************************************************************************************************************************** +Variant Data Type +***********************************************************************************************************************************/ +#ifndef COMMON_TYPE_VARIANT_H +#define COMMON_TYPE_VARIANT_H + +#include + +/*********************************************************************************************************************************** +Variant type +***********************************************************************************************************************************/ +typedef enum +{ + varTypeBool, + varTypeDouble, + varTypeInt, + varTypeKeyValue, + varTypeString, + varTypeVariantList, +} VariantType; + +/*********************************************************************************************************************************** +Variant object +***********************************************************************************************************************************/ +typedef struct Variant Variant; + +/*********************************************************************************************************************************** +Functions +***********************************************************************************************************************************/ +#include "common/type/string.h" +#include "common/type/variantList.h" + +Variant *varNewBool(bool data); +bool varBool(const Variant *this); +bool varBoolForce(const Variant *this); + +Variant *varNewDbl(double data); +double varDbl(const Variant *this); +double varDblForce(const Variant *this); + +Variant *varNewInt(int data); +int varInt(const Variant *this); +int varIntForce(const Variant *this); + +Variant *varNewStr(const String *data); +Variant *varNewStrZ(const char *data); +String *varStr(const Variant *this); + +Variant *varNewVarLst(const VariantList *data); +Variant *varNewVarLstEmpty(); +VariantList *varVarLst(const Variant *this); + +Variant *varDup(const Variant *this); +bool varEq(const Variant *this1, const Variant *this2); +VariantType varType(const Variant *this); + +void varFree(Variant *this); + +#endif diff --git a/src/common/type/variantList.c b/src/common/type/variantList.c new file mode 100644 index 000000000..a7b9b0e78 --- /dev/null +++ b/src/common/type/variantList.c @@ -0,0 +1,92 @@ +/*********************************************************************************************************************************** +Variant List Handler +***********************************************************************************************************************************/ +#include +#include +#include + +#include "common/memContext.h" +#include "common/type/list.h" +#include "common/type/variantList.h" + +/*********************************************************************************************************************************** +Wrapper for lstNew() +***********************************************************************************************************************************/ +VariantList * +varLstNew() +{ + return (VariantList *)lstNew(sizeof(Variant *)); +} + +/*********************************************************************************************************************************** +Create a variant list from a string list +***********************************************************************************************************************************/ +VariantList * +varLstNewStrLst(const StringList *stringList) +{ + VariantList *result = NULL; + + if (stringList != NULL) + { + result = varLstNew(); + + for (unsigned int listIdx = 0; listIdx < strLstSize(stringList); listIdx++) + varLstAdd(result, varNewStr(strLstGet(stringList, listIdx))); + } + + return result; +} + +/*********************************************************************************************************************************** +Duplicate a variant list +***********************************************************************************************************************************/ +VariantList * +varLstDup(const VariantList *source) +{ + VariantList *result = NULL; + + if (source != NULL) + { + result = varLstNew(); + + for (unsigned int listIdx = 0; listIdx < varLstSize(source); listIdx++) + varLstAdd(result, varDup(varLstGet(source, listIdx))); + } + + return result; +} + +/*********************************************************************************************************************************** +Wrapper for lstAdd() +***********************************************************************************************************************************/ +VariantList * +varLstAdd(VariantList *this, Variant *data) +{ + return (VariantList *)lstAdd((List *)this, &data); +} + +/*********************************************************************************************************************************** +Wrapper for lstGet() +***********************************************************************************************************************************/ +Variant * +varLstGet(const VariantList *this, unsigned int listIdx) +{ + return *(Variant **)lstGet((List *)this, listIdx); +} + +/*********************************************************************************************************************************** +Wrapper for lstSize() +***********************************************************************************************************************************/ +unsigned int +varLstSize(const VariantList *this) +{ + return lstSize((List *)this); +} + +/*********************************************************************************************************************************** +Wrapper for lstFree() +***********************************************************************************************************************************/ +void varLstFree(VariantList *this) +{ + lstFree((List *)this); +} diff --git a/src/common/type/variantList.h b/src/common/type/variantList.h new file mode 100644 index 000000000..8cba40385 --- /dev/null +++ b/src/common/type/variantList.h @@ -0,0 +1,25 @@ +/*********************************************************************************************************************************** +Variant List Handler +***********************************************************************************************************************************/ +#ifndef COMMON_TYPE_VARIANT_LIST_H +#define COMMON_TYPE_VARIANT_LIST_H + +/*********************************************************************************************************************************** +Variant list type +***********************************************************************************************************************************/ +typedef struct VariantList VariantList; + +/*********************************************************************************************************************************** +Functions +***********************************************************************************************************************************/ +#include "common/type/variant.h" + +VariantList *varLstNew(); +VariantList *varLstNewStrLst(const StringList *stringList); +VariantList *varLstDup(const VariantList *source); +VariantList *varLstAdd(VariantList *this, Variant *data); +Variant *varLstGet(const VariantList *this, unsigned int listIdx); +unsigned int varLstSize(const VariantList *this); +void varLstFree(VariantList *this); + +#endif diff --git a/test/lib/pgBackRestTest/Common/DefineTest.pm b/test/lib/pgBackRestTest/Common/DefineTest.pm index dea59be60..1abc637b4 100644 --- a/test/lib/pgBackRestTest/Common/DefineTest.pm +++ b/test/lib/pgBackRestTest/Common/DefineTest.pm @@ -153,7 +153,7 @@ my $oTestDef = }, { &TESTDEF_NAME => 'type-string-list', - &TESTDEF_TOTAL => 4, + &TESTDEF_TOTAL => 5, &TESTDEF_C => true, &TESTDEF_COVERAGE => @@ -171,6 +171,26 @@ my $oTestDef = 'common/type/buffer' => TESTDEF_COVERAGE_FULL, }, }, + { + &TESTDEF_NAME => 'type-variant', + &TESTDEF_TOTAL => 5, + &TESTDEF_C => true, + + &TESTDEF_COVERAGE => + { + 'common/type/variant' => TESTDEF_COVERAGE_FULL, + }, + }, + { + &TESTDEF_NAME => 'type-variant-list', + &TESTDEF_TOTAL => 3, + &TESTDEF_C => true, + + &TESTDEF_COVERAGE => + { + 'common/type/variantList' => TESTDEF_COVERAGE_FULL, + }, + }, { &TESTDEF_NAME => 'encode', &TESTDEF_TOTAL => 1, diff --git a/test/src/module/common/typeStringListTest.c b/test/src/module/common/typeStringListTest.c index 5a044437b..7dc33dde2 100644 --- a/test/src/module/common/typeStringListTest.c +++ b/test/src/module/common/typeStringListTest.c @@ -73,6 +73,19 @@ void testRun() strLstFree(list); } + // ***************************************************************************************************************************** + if (testBegin("strLstNewVarLst()")) + { + VariantList *varList = varLstNew(); + + varLstAdd(varList, varNewStr(strNew("string1"))); + varLstAdd(varList, varNewStr(strNew("string2"))); + + TEST_RESULT_STR(strPtr(strLstJoin(strLstNewVarLst(varList), ", ")), "string1, string2", "string list from variant list"); + + varLstFree(varList); + } + // ***************************************************************************************************************************** if (testBegin("strLstJoin()")) { diff --git a/test/src/module/common/typeVariantListTest.c b/test/src/module/common/typeVariantListTest.c new file mode 100644 index 000000000..2d3011d69 --- /dev/null +++ b/test/src/module/common/typeVariantListTest.c @@ -0,0 +1,57 @@ +/*********************************************************************************************************************************** +Test Variant Lists +***********************************************************************************************************************************/ + +/*********************************************************************************************************************************** +Test Run +***********************************************************************************************************************************/ +void testRun() +{ + // ***************************************************************************************************************************** + if (testBegin("varLstNew(), varLstAdd(), varLstSize(), varLstGet(), and varListFree()")) + { + VariantList *list = NULL; + + TEST_ASSIGN(list, varLstNew(), "new list"); + + TEST_RESULT_PTR(varLstAdd(list, varNewInt(27)), list, "add int"); + TEST_RESULT_PTR(varLstAdd(list, varNewStr(strNew("test-str"))), list, "add string"); + + TEST_RESULT_INT(varLstSize(list), 2, "list size"); + + TEST_RESULT_INT(varInt(varLstGet(list, 0)), 27, "check int"); + TEST_RESULT_STR(strPtr(varStr(varLstGet(list, 1))), "test-str", "check string"); + + TEST_RESULT_VOID(varLstFree(list), "free list"); + } + + // ***************************************************************************************************************************** + if (testBegin("varLstDup()")) + { + VariantList *list = varLstNew(); + + varLstAdd(list, varNewStr(strNew("string1"))); + varLstAdd(list, varNewStr(strNew("string2"))); + + TEST_RESULT_STR( + strPtr(strLstJoin(strLstNewVarLst(varLstDup(list)), ", ")), + "string1, string2", "duplicate variant list"); + + TEST_RESULT_PTR(varLstDup(NULL), NULL, "duplicate null list"); + } + + // ***************************************************************************************************************************** + if (testBegin("varLstNewStrLst()")) + { + StringList *listStr = strLstNew(); + + strLstAdd(listStr, strNew("string1")); + strLstAdd(listStr, strNew("string2")); + + TEST_RESULT_STR( + strPtr(strLstJoin(strLstNewVarLst(varLstNewStrLst(listStr)), ", ")), + "string1, string2", "variant list from string list"); + + TEST_RESULT_PTR(varLstNewStrLst(NULL), NULL, "variant list from null string list"); + } +} diff --git a/test/src/module/common/typeVariantTest.c b/test/src/module/common/typeVariantTest.c new file mode 100644 index 000000000..f9868e288 --- /dev/null +++ b/test/src/module/common/typeVariantTest.c @@ -0,0 +1,195 @@ +/*********************************************************************************************************************************** +Test Variant Data Type +***********************************************************************************************************************************/ + +/*********************************************************************************************************************************** +Test Run +***********************************************************************************************************************************/ +void testRun() +{ + // ----------------------------------------------------------------------------------------------------------------------------- + if (testBegin("bool")) + { + Variant *boolean = varNewBool(false); + + boolean->type = varTypeString; + TEST_ERROR(varBool(boolean), AssertError, "variant type is not bool"); + + boolean->type = varTypeBool; + varFree(boolean); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_BOOL(varBool(varNewBool(true)), true, "true bool variant"); + TEST_RESULT_BOOL(varBool(varNewBool(false)), false, "false bool variant"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_BOOL(varBoolForce(varNewBool(false)), false, "force bool to bool"); + TEST_RESULT_BOOL(varBoolForce(varNewInt(1)), true, "force int to bool"); + + TEST_ERROR(varBoolForce(varNewVarLstEmpty()), FormatError, "unable to force VariantList to bool"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_VOID(varFree(NULL), "ok to free null variant"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_BOOL(varBool(varDup(varNewBool(true))), true, "dup bool"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_BOOL(varEq(varNewBool(true), varNewInt(1)), false, "bool, int not eq"); + + TEST_RESULT_BOOL(varEq(varNewBool(true), varNewBool(true)), true, "bool, bool eq"); + TEST_RESULT_BOOL(varEq(varNewBool(false), varNewBool(true)), false, "bool, bool not eq"); + } + + // ----------------------------------------------------------------------------------------------------------------------------- + if (testBegin("double")) + { + Variant *var = varNewDbl(44.44); + TEST_RESULT_DOUBLE(varDbl(var), 44.44, "double variant"); + varFree(var); + + // ------------------------------------------------------------------------------------------------------------------------- + var = varNewDbl(-1.1); + + var->type = varTypeString; + TEST_ERROR(varDbl(var), AssertError, "variant type is not double"); + + var->type = varTypeDouble; + varFree(var); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_DOUBLE(varDblForce(varNewDbl(4.567)), 4.567, "force double to double"); + TEST_RESULT_DOUBLE(varDblForce(varNewBool(false)), 0, "force bool to double"); + TEST_RESULT_DOUBLE(varDblForce(varNewInt(123)), 123, "force int to double"); + TEST_RESULT_DOUBLE(varDblForce(varNewStr(strNew("879.01"))), 879.01, "force String to double"); + + TEST_ERROR(varDblForce(varNewStr(strNew("AAA"))), FormatError, "unable to force String 'AAA' to double"); + TEST_ERROR(varDblForce(varNewVarLstEmpty()), FormatError, "unable to force VariantList to double"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_DOUBLE(varDbl(varDup(varNewDbl(3.1415))), 3.1415, "dup double"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_BOOL(varEq(varNewDbl(1.234), varNewDbl(1.234)), true, "double, double eq"); + TEST_RESULT_BOOL(varEq(varNewDbl(4.321), varNewDbl(1.234)), false, "double, double not eq"); + } + + // ----------------------------------------------------------------------------------------------------------------------------- + if (testBegin("int")) + { + Variant *integer = varNewInt(44); + TEST_RESULT_INT(varInt(integer), 44, "int variant"); + TEST_RESULT_INT(varIntForce(integer), 44, "force int to int"); + varFree(integer); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_INT(varIntForce(varNewBool(true)), 1, "force bool to int"); + TEST_ERROR(varIntForce(varNewVarLstEmpty()), FormatError, "unable to force VariantList to int"); + + // ------------------------------------------------------------------------------------------------------------------------- + integer = varNewInt(-1); + + integer->type = varTypeString; + TEST_ERROR(varInt(integer), AssertError, "variant type is not int"); + + integer->type = varTypeInt; + varFree(integer); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_INT(varInt(varDup(varNewInt(88976))), 88976, "dup int"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_BOOL(varEq(NULL, NULL), true, "null, null eq"); + TEST_RESULT_BOOL(varEq(NULL, varNewInt(123)), false, "null, int not eq"); + + TEST_RESULT_BOOL(varEq(varNewInt(123), varNewInt(123)), true, "int, int eq"); + TEST_RESULT_BOOL(varEq(varNewInt(444), varNewInt(123)), false, "int, int not eq"); + } + + // ----------------------------------------------------------------------------------------------------------------------------- + if (testBegin("String")) + { + TEST_ERROR(varNewStr(NULL), AssertError, "string variant cannot be NULL"); + + // ------------------------------------------------------------------------------------------------------------------------- + Variant *string = varNewStr(strNew("test-str")); + TEST_RESULT_STR(strPtr(varStr(string)), "test-str", "string pointer"); + varFree(string); + + // ------------------------------------------------------------------------------------------------------------------------- + string = varNewStr(strNew("not-a-string")); + string->type = varTypeInt; + TEST_ERROR(strPtr(varStr(string)), AssertError, "variant type is not string"); + varFree(string); + + // ------------------------------------------------------------------------------------------------------------------------- + string = varNewStr(strNew("777")); + TEST_RESULT_INT(varIntForce(string), 777, "int from string"); + varFree(string); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_INT(varBoolForce(varNewStr(strNew("y"))), true, "bool from string"); + TEST_RESULT_INT(varBoolForce(varNewStr(strNew("on"))), true, "bool from string"); + + TEST_RESULT_INT(varBoolForce(varNewStr(strNew("n"))), false, "bool from string"); + TEST_RESULT_INT(varBoolForce(varNewStr(strNew("off"))), false, "bool from string"); + + TEST_RESULT_INT(varBoolForce(varNewStr(strNew("OFF"))), false, "bool from string"); + + TEST_ERROR(varBoolForce(varNewStr(strNew(BOGUS_STR))), FormatError, "unable to convert str 'BOGUS' to bool"); + + // ------------------------------------------------------------------------------------------------------------------------- + string = varNewStr(strNew("not-an-int")); + TEST_ERROR(varIntForce(string), FormatError, "unable to convert str 'not-an-int' to int"); + varFree(string); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_STR(strPtr(varStr(varNewStrZ("test-z-str"))), "test-z-str", "new zero-terminated string"); + TEST_ERROR(varNewStrZ(NULL), AssertError, "zero-terminated string cannot be NULL"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_STR(strPtr(varStr(varDup(varNewStr(strNew("yabba-dabba-doo"))))), "yabba-dabba-doo", "dup string"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_RESULT_PTR(varDup(NULL), NULL, "dup NULL"); + TEST_RESULT_BOOL(varEq(varNewStr(strNew("expect-equal")), varNewStr(strNew("expect-equal"))), true, "string, string eq"); + TEST_RESULT_BOOL(varEq(varNewStr(strNew("Y")), varNewStr(strNew("X"))), false, "string, string not eq"); + } + + // ----------------------------------------------------------------------------------------------------------------------------- + if (testBegin("VariantList")) + { + TEST_ERROR(varVarLst(varNewInt(66)), AssertError, "variant type is not 'VariantList'"); + + // ------------------------------------------------------------------------------------------------------------------------- + Variant *listVar = NULL; + + TEST_ASSIGN(listVar, varNewVarLstEmpty(), "new empty"); + + TEST_RESULT_INT(varLstSize(varVarLst(listVar)), 0, " empty size"); + + TEST_RESULT_PTR(varLstAdd(varVarLst(listVar), varNewBool(true)), varVarLst(listVar), " add bool"); + TEST_RESULT_PTR(varLstAdd(varVarLst(listVar), varNewInt(55)), varVarLst(listVar), " add int"); + + TEST_RESULT_INT(varLstSize(varVarLst(listVar)), 2, " size with items"); + + TEST_RESULT_BOOL(varBool(varLstGet(varVarLst(listVar), 0)), true, " get bool"); + TEST_RESULT_INT(varInt(varLstGet(varVarLst(listVar), 1)), 55, " get int"); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_ASSIGN(listVar, varNewVarLst(varVarLst(listVar)), "new with items"); + TEST_RESULT_INT(varLstSize(varVarLst(listVar)), 2, " size with items"); + + // ------------------------------------------------------------------------------------------------------------------------- + Variant *listVarDup = NULL; + + TEST_ASSIGN(listVarDup, varDup(listVar), "duplicate"); + TEST_RESULT_INT(varLstSize(varVarLst(listVarDup)), 2, " size with items"); + + varFree(listVar); + varFree(listVarDup); + + // ------------------------------------------------------------------------------------------------------------------------- + TEST_ERROR(varEq(varNewVarLstEmpty(), varNewVarLstEmpty()), AssertError, "unable to test equality for VariantList"); + } +}