884 lines
29 KiB
MQL5
884 lines
29 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| DomTest.mqh |
|
|
//| Copyright 2026, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2026, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property strict
|
|
|
|
#ifndef JSONPARSERBYLEO_WF_INC_DOMTEST_MQH
|
|
#define JSONPARSERBYLEO_WF_INC_DOMTEST_MQH
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
#include "Def.mqh"
|
|
#resource "dom_test.json" as const string g_dom_test_json
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
namespace TSN
|
|
{
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 1. ToDom() sobre objeto raiz valido retorna true y crea el root |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_ToDomBasicSucceeds : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_ToDom_Basic_Succeeds"; }
|
|
inline int Importance() const override final { return 95; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
bool ok = parser.GetRoot().ToDom(root, &manager);
|
|
|
|
if(!ok || root == NULL)
|
|
{
|
|
msg = "ToDom() fallo o root quedo NULL";
|
|
return false;
|
|
}
|
|
delete root;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 2. ToDom() preserva valores escalares (string/double/bool/int) |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_ToDomPreservesScalars : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_ToDom_PreservesScalars"; }
|
|
inline int Importance() const override final { return 95; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
string symbol = root["symbol"].ToString("__MISSING__");
|
|
double bid = root["bid"].ToDouble(-1.0);
|
|
bool active = root["active"].ToBool(false);
|
|
long neg = root["neg"].ToInt(0);
|
|
|
|
bool fail = (symbol != "EURUSD" || MathAbs(bid - 1.2345) > 0.0001 || !active || neg != -42);
|
|
delete root;
|
|
|
|
if(fail)
|
|
{
|
|
msg = StringFormat("valores no preservados tras ToDom: symbol=%s bid=%.4f active=%s neg=%d",
|
|
symbol, bid, active ? "true" : "false", neg);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 3. ToDom() preserva anidamiento (meta.nested.deep) |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_ToDomPreservesNesting : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_ToDom_PreservesNesting"; }
|
|
inline int Importance() const override final { return 90; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
//parser.PrintCintaTypes(0,WHOLE_ARRAY);
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
//TSN::CJsonDomSerializer ser;
|
|
if(!parser.GetRoot().ToDom(root, &manager))
|
|
{
|
|
msg = "Fallo al pasar a dom";
|
|
return false;
|
|
}
|
|
//ser.Serialize(root);
|
|
//Print(CharArrayToString(ser.m_buf, 0, ser.m_buf_s));
|
|
|
|
|
|
string deep = root["meta"]["nested"]["deep"].ToString("__MISSING__");
|
|
delete root;
|
|
|
|
if(deep != "ok")
|
|
{
|
|
msg = StringFormat("esperado 'ok', obtenido '%s'", deep);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 4. ToDom() preserva arrays anidados: root["ae"].At(1).At(1) |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_ToDomPreservesNestedArray : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_ToDom_PreservesNestedArray"; }
|
|
inline int Importance() const override final { return 90; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
long val = root["ae"].At(1).At(1).ToInt(-1);
|
|
delete root;
|
|
if(val != 20)
|
|
{
|
|
msg = StringFormat("esperado 20, obtenido %d", val);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 5. ToDom() sobre un valor escalar (no obj/arr) falla (false) |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_ToDomOnScalarFails : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_ToDom_OnScalarNode_Fails"; }
|
|
inline int Importance() const override final { return 75; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* leaf = NULL;
|
|
TSN::CDomNodeManager manager;
|
|
// "symbol" es un STRING escalar, no OBJ/ARR -> debe fallar
|
|
bool ok = parser.GetRoot()["symbol"].ToDom(leaf, &manager);
|
|
if(ok)
|
|
{
|
|
msg = "ToDom() sobre nodo escalar devolvio true (esperado false)";
|
|
if(leaf != NULL)
|
|
delete leaf;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 6. GetKeys()/Size() del DOM tras ToDom() coinciden con el origina|
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_ToDomKeysAndSizeMatch : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_ToDom_KeysAndSize_Match"; }
|
|
inline int Importance() const override final { return 85; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
string keys[];
|
|
int count = root.GetKeys(keys);
|
|
int size = root.Size();
|
|
delete root;
|
|
|
|
if(count != size || count <= 0)
|
|
{
|
|
msg = StringFormat("GetKeys count=%d, Size()=%d (deberian coincidir y ser > 0)", count, size);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 7. root["nueva_key"] = double crea la clave y cambia Size() |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_MutateAddNewKey : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Mutate_AddNewKey_Double"; }
|
|
inline int Importance() const override final { return 90; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
const int before = root.Size();
|
|
root["sumando"] = 20.0;
|
|
const int after = root.Size();
|
|
double val = root["sumando"].ToDouble(-1.0);
|
|
|
|
delete root;
|
|
|
|
if(after != before + 1 || MathAbs(val - 20.0) > 0.0001)
|
|
{
|
|
msg = StringFormat("esperado size+1 y val=20.0, obtenido before=%d after=%d val=%.4f", before, after, val);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 8. operator= cambia el tipo dinamicamente (double -> bool) |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_MutateChangeTypeDoubleToBool : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Mutate_ChangeType_DoubleToBool"; }
|
|
inline int Importance() const override final { return 90; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
root["sumando"] = 20.0;
|
|
root["sumando"] = true; // cambia de FLT a BOL
|
|
bool val = root["sumando"].ToBool(false);
|
|
|
|
delete root;
|
|
|
|
if(!val)
|
|
{
|
|
msg = "tras cambiar a bool, ToBool(false) devolvio false (esperado true)";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 9. Cambiar un OBJ/ARR a escalar libera el contenedor previo |
|
|
//| (no debe crashear ni dejar referencias colgantes accesibles) |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_MutateObjectToScalarFreesContainer : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Mutate_ObjectToScalar_FreesContainer"; }
|
|
inline int Importance() const override final { return 80; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
// "meta" es un objeto anidado; lo convertimos a string
|
|
// operator[] devuelve el puntero real -> operator=(string&) libera el
|
|
// OBJ previo internamente (ver CDomNodeBase::operator=(const string&))
|
|
|
|
bool assign_ok = (root["meta"].As("reemplazado"));
|
|
|
|
string val = root["meta"].ToString("__MISSING__");
|
|
delete root;
|
|
|
|
if(!assign_ok || val != "reemplazado")
|
|
{
|
|
msg = StringFormat("esperado 'reemplazado', obtenido '%s' (assign_ok=%s)", val, assign_ok ? "true" : "false");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 10. Remove(key) elimina la clave y reduce Size() |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_RemoveKeyReducesSize : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Remove_Key_ReducesSize"; }
|
|
inline int Importance() const override final { return 85; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
const int before = root.Size();
|
|
bool removed = root.Remove("symbol");
|
|
const int after = root.Size();
|
|
bool still_has = root.HasKey("symbol");
|
|
|
|
delete root;
|
|
|
|
if(!removed || after != before - 1 || still_has)
|
|
{
|
|
msg = StringFormat("removed=%s before=%d after=%d still_has=%s",
|
|
removed ? "true" : "false", before, after, still_has ? "true" : "false");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 11. Remove(key) sobre clave inexistente devuelve false |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_RemoveMissingKeyReturnsFalse : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Remove_MissingKey_ReturnsFalse"; }
|
|
inline int Importance() const override final { return 70; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
bool removed = root.Remove("no_existe_esta_clave");
|
|
delete root;
|
|
|
|
if(removed)
|
|
{
|
|
msg = "Remove() sobre clave inexistente devolvio true";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 12. Remove(index) sobre array elimina el elemento y reduce Size |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_RemoveIndexReducesArraySize : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Remove_Index_ReducesArraySize"; }
|
|
inline int Importance() const override final { return 80; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
CDomNodeBase* precios = root["precios"]; // [10,20,30,40,50]
|
|
const int before = precios.Size();
|
|
bool removed = precios.RemoveItem(0);
|
|
const int after = precios.Size();
|
|
|
|
delete root;
|
|
|
|
if(!removed || after != before - 1)
|
|
{
|
|
msg = StringFormat("removed=%s before=%d after=%d", removed ? "true" : "false", before, after);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 13. AddItem() en array standalone incrementa Size() |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_AddItemIncreasesSize : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_AddItem_IncreasesSize"; }
|
|
inline int Importance() const override final { return 80; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CDomNodeManager manager;
|
|
TSN::CDomNodeBase* arr = new TSN::CDomNodeBase(&manager);
|
|
arr.NewArr(4);
|
|
|
|
arr.AddItem(new TSN::CDomNodeBase(&manager, TSN_SBL_BASE_TYPE_INT, 1));
|
|
arr.AddItem(new TSN::CDomNodeBase(&manager, TSN_SBL_BASE_TYPE_INT, 2));
|
|
|
|
const int size = arr.Size();
|
|
long first = arr.At(0).ToInt(-1);
|
|
long second = arr.At(1).ToInt(-1);
|
|
|
|
delete arr;
|
|
|
|
if(size != 2 || first != 1 || second != 2)
|
|
{
|
|
msg = StringFormat("esperado size=2 [1,2], obtenido size=%d [%d,%d]", size, first, second);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 14. InRange() valida correctamente indices de array |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_InRangeValidatesIndices : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_InRange_ValidatesIndices"; }
|
|
inline int Importance() const override final { return 75; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
CDomNodeBase* precios = root["precios"]; // 5 elementos
|
|
bool in_range_ok = precios.InRange(4);
|
|
bool in_range_bad = precios.InRange(99);
|
|
|
|
delete root;
|
|
|
|
if(!in_range_ok || in_range_bad)
|
|
{
|
|
msg = StringFormat("InRange(4)=%s InRange(99)=%s", in_range_ok ? "true" : "false", in_range_bad ? "true" : "false");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 15. AtSafe() fuera de rango devuelve NULL sin crashear |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_AtSafeOutOfRangeReturnsNull : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_AtSafe_OutOfRange_ReturnsNull"; }
|
|
inline int Importance() const override final { return 80; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
CDomNodeBase* precios = root["precios"];
|
|
CDomNodeBase* out = precios.AtSafe(999);
|
|
|
|
delete root;
|
|
|
|
if(out != NULL)
|
|
{
|
|
msg = "AtSafe(999) fuera de rango no devolvio NULL";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 16. Clear() sobre objeto elimina todas sus claves (Size() -> 0) |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_ClearObjectEmptiesIt : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Clear_Object_EmptiesIt"; }
|
|
inline int Importance() const override final { return 80; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
CDomNodeBase* meta = root["meta"];
|
|
meta.Clear();
|
|
const int size_after = meta.Size();
|
|
|
|
delete root;
|
|
|
|
if(size_after != 0)
|
|
{
|
|
msg = StringFormat("esperado Size()=0 tras Clear(), obtenido %d", size_after);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 17. NewObj + Set + operator[] construyen un objeto correctamente |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_StandaloneNewObjSetGet : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Standalone_NewObj_SetGet"; }
|
|
inline int Importance() const override final { return 90; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CDomNodeManager manager;
|
|
TSN::CDomNodeBase* root = new TSN::CDomNodeBase(&manager);
|
|
root.NewObj(8);
|
|
|
|
root["a"] = (long)1;
|
|
root["b"].As("hola");
|
|
root["c"] = true;
|
|
|
|
long a = root["a"].ToInt(-1);
|
|
string b = root["b"].ToString("__MISSING__");
|
|
bool c = root["c"].ToBool(false);
|
|
int size = root.Size();
|
|
|
|
delete root;
|
|
|
|
if(a != 1 || b != "hola" || !c || size != 3)
|
|
{
|
|
msg = StringFormat("a=%d b=%s c=%s size=%d", a, b, c ? "true" : "false", size);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 18. NewArr + AddItem construyen un array homogeneo correctamente |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_StandaloneNewArrAddItem : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Standalone_NewArr_AddItem"; }
|
|
inline int Importance() const override final { return 90; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CDomNodeManager manager;
|
|
TSN::CDomNodeBase* arr = new TSN::CDomNodeBase(&manager);
|
|
arr.NewArr(8);
|
|
|
|
for(int i = 0; i < 5; i++)
|
|
arr.AddItem(new TSN::CDomNodeBase(&manager, TSN_SBL_BASE_TYPE_INT, (long)(i * 10)));
|
|
|
|
int size = arr.Size();
|
|
long third = arr.At(2).ToInt(-1);
|
|
|
|
delete arr;
|
|
|
|
if(size != 5 || third != 20)
|
|
{
|
|
msg = StringFormat("esperado size=5 third=20, obtenido size=%d third=%d", size, third);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 19. ToArray<long> extrae un array standalone a un array MQL5 |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_StandaloneToArray : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_Standalone_ToArray"; }
|
|
inline int Importance() const override final { return 80; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CDomNodeManager manager;
|
|
TSN::CDomNodeBase* arr = new TSN::CDomNodeBase(&manager);
|
|
arr.NewArr(4);
|
|
arr.AddItem(new TSN::CDomNodeBase(&manager, TSN_SBL_BASE_TYPE_INT, (long)7));
|
|
arr.AddItem(new TSN::CDomNodeBase(&manager, TSN_SBL_BASE_TYPE_INT, (long)8));
|
|
arr.AddItem(new TSN::CDomNodeBase(&manager, TSN_SBL_BASE_TYPE_INT, (long)9));
|
|
|
|
long out[];
|
|
int count = arr.ToArray(out);
|
|
|
|
delete arr;
|
|
|
|
if(count != 3 || out[0] != 7 || out[2] != 9)
|
|
{
|
|
msg = StringFormat("esperado count=3 [7,..,9], obtenido count=%d", count);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 20. Patron multi-manager: Set() con puntero de OTRO manager no lo |
|
|
//| copia (comparten el puntero), y cada manager libera solo lo |
|
|
//| que es suyo — replica exacto del patron del README/Dom.mq5. |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDom_MultiManagerSetSharesPointer : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "Dom_MultiManager_Set_SharesPointer_NoDoubleFree"; }
|
|
inline int Importance() const override final { return 85; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CDomNodeManager manager1;
|
|
TSN::CDomNodeBase* root = new TSN::CDomNodeBase(&manager1);
|
|
root.NewObj(8);
|
|
root["a"] = (long)1;
|
|
|
|
//--- Segundo manager y arbol independiente
|
|
TSN::CDomNodeManager manager2;
|
|
TSN::CDomNodeBase* ptr = new TSN::CDomNodeBase(&manager2);
|
|
ptr.NewObj(4);
|
|
ptr["valores"] = 20.0;
|
|
|
|
//--- Colgamos ptr (de manager2) dentro de root (de manager1) sin copia profunda
|
|
bool set_ok = root.Set("nueva_key", ptr);
|
|
ptr["coma"] = true; // seguimos pudiendo mutar ptr normalmente
|
|
|
|
double valores = root["nueva_key"]["valores"].ToDouble(-1.0);
|
|
bool coma = root["nueva_key"]["coma"].ToBool(false);
|
|
|
|
//--- Cada manager libera SOLO lo suyo: como root.m_ctx == manager1 y
|
|
// ptr.m_ctx == manager2, 'delete root' NO debe liberar 'ptr' (evita
|
|
// doble free); hay que liberarlo explicitamente aparte.
|
|
delete root;
|
|
delete ptr;
|
|
|
|
if(!set_ok || MathAbs(valores - 20.0) > 0.0001 || !coma)
|
|
{
|
|
msg = StringFormat("set_ok=%s valores=%.4f coma=%s", set_ok ? "true" : "false", valores, coma ? "true" : "false");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 21. Serialize() de un objeto standalone simple produce JSON valid|
|
|
//+------------------------------------------------------------------+
|
|
class CTestDomSerializer_SimpleObject : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "DomSerializer_SimpleObject"; }
|
|
inline int Importance() const override final { return 90; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CDomNodeManager manager;
|
|
TSN::CDomNodeBase* root = new TSN::CDomNodeBase(&manager);
|
|
root.NewObj(4);
|
|
root["id"] = (long)1;
|
|
root["active"] = true;
|
|
|
|
TSN::CJsonDomSerializer serializer;
|
|
bool ok = serializer.Serialize(root);
|
|
string out = CharArrayToString(serializer.m_buf, 0, serializer.m_buf_s);
|
|
|
|
delete root;
|
|
|
|
if(!ok || out != "{\"id\":1,\"active\":true}")
|
|
{
|
|
msg = StringFormat("ok=%s obtenido '%s'", ok ? "true" : "false", out);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 22. Serialize() escapa strings correctamente (comillas/newline) |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDomSerializer_EscapesStrings : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "DomSerializer_EscapesStrings"; }
|
|
inline int Importance() const override final { return 85; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CDomNodeManager manager;
|
|
TSN::CDomNodeBase* root = new TSN::CDomNodeBase(&manager);
|
|
root.NewObj(4);
|
|
root["k"].As("line1\nline2 \"quoted\"");
|
|
|
|
TSN::CJsonDomSerializer serializer;
|
|
serializer.Serialize(root);
|
|
string out = CharArrayToString(serializer.m_buf, 0, serializer.m_buf_s);
|
|
|
|
delete root;
|
|
|
|
if(out != "{\"k\":\"line1\\nline2 \\\"quoted\\\"\"}")
|
|
{
|
|
msg = StringFormat("obtenido '%s'", out);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 23. Serialize() de array anidado produce estructura correcta |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDomSerializer_NestedArray : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "DomSerializer_NestedArray"; }
|
|
inline int Importance() const override final { return 85; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CDomNodeManager manager;
|
|
TSN::CDomNodeBase* root = new TSN::CDomNodeBase(&manager);
|
|
root.NewArr(4);
|
|
root.AddItem(new TSN::CDomNodeBase(&manager, TSN_SBL_BASE_TYPE_INT, (long)1));
|
|
root.AddItem(new TSN::CDomNodeBase(&manager, TSN_SBL_BASE_TYPE_INT, (long)2));
|
|
|
|
TSN::CJsonDomSerializer serializer;
|
|
serializer.Serialize(root);
|
|
string out = CharArrayToString(serializer.m_buf, 0, serializer.m_buf_s);
|
|
|
|
delete root;
|
|
|
|
if(out != "[1,2]")
|
|
{
|
|
msg = StringFormat("obtenido '%s'", out);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 24. Serialize() de null produce el literal "null" |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDomSerializer_NullValue : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "DomSerializer_NullValue"; }
|
|
inline int Importance() const override final { return 75; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CDomNodeManager manager;
|
|
TSN::CDomNodeBase* root = new TSN::CDomNodeBase(&manager);
|
|
root.NewObj(4);
|
|
root["k"]; // operator[] crea el nodo hijo como NULL (TSN_SBL_BASE_DOM_NULL) por defecto
|
|
|
|
TSN::CJsonDomSerializer serializer;
|
|
serializer.Serialize(root);
|
|
string out = CharArrayToString(serializer.m_buf, 0, serializer.m_buf_s);
|
|
|
|
delete root;
|
|
|
|
if(out != "{\"k\":null}")
|
|
{
|
|
msg = StringFormat("obtenido '%s'", out);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 25. Round-trip completo: Parse -> ToDom -> mutar -> Serialize -> |
|
|
//| Reparsear el resultado y verificar los valores mutados |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDomSerializer_FullRoundTripWithMutation : public ICiTest
|
|
{
|
|
public:
|
|
inline string Name() const override final { return "DomSerializer_FullRoundTrip_WithMutation"; }
|
|
inline int Importance() const override final { return 90; }
|
|
bool Run(string &msg) override final
|
|
{
|
|
TSN::CJsonParser parser;
|
|
parser.Assing(g_dom_test_json);
|
|
parser.CorrectPadding();
|
|
parser.Parse();
|
|
|
|
TSN::CDomNodeBase* root;
|
|
TSN::CDomNodeManager manager;
|
|
parser.GetRoot().ToDom(root, &manager);
|
|
|
|
//--- Mutamos: agregamos una clave nueva
|
|
root["sumando"] = 20.0;
|
|
|
|
//--- Serializamos de vuelta a texto
|
|
TSN::CJsonDomSerializer serializer;
|
|
serializer.Serialize(root);
|
|
string out = CharArrayToString(serializer.m_buf, 0, serializer.m_buf_s);
|
|
delete root;
|
|
|
|
//--- Reparseamos el resultado con un parser nuevo, independiente
|
|
TSN::CJsonParser p2;
|
|
p2.Assing(out);
|
|
p2.CorrectPadding();
|
|
if(!p2.Parse())
|
|
{
|
|
msg = StringFormat("el JSON serializado no es reparseable: '%s'", out);
|
|
return false;
|
|
}
|
|
|
|
double sumando = p2.GetRoot()["sumando"].ToDouble(-1.0);
|
|
string symbol = p2.GetRoot()["symbol"].ToString("__MISSING__");
|
|
|
|
if(MathAbs(sumando - 20.0) > 0.0001 || symbol != "EURUSD")
|
|
{
|
|
msg = StringFormat("tras round-trip completo: sumando=%.4f symbol=%s", sumando, symbol);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
#endif // JSONPARSERBYLEO_WF_INC_DOMTEST_MQH
|