JsonParserByLeo/Wf/Inc/BuilderTest.mqh
Nique_372 e752ef67b3
2026-07-21 10:38:19 -05:00

675 lines
22 KiB
MQL5

//+------------------------------------------------------------------+
//| BuilderTest.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_BUILDERTEST_MQH
#define JSONPARSERBYLEO_WF_INC_BUILDERTEST_MQH
//+------------------------------------------------------------------+
//| Tests de CJsonBuilder (JSON valido) y CJsonBuilderStr (JSON |
//| doblemente escapado, para meter un JSON como valor string). |
//| No requieren parser ni fixture: cada test construye su propio |
//| builder local y compara el string de salida via Build(). |
//+------------------------------------------------------------------+
#include "Def.mqh"
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
namespace TSN
{
//======================================================================
// SECCION 1: CJsonBuilder (JSON valido, escape simple)
//======================================================================
//+------------------------------------------------------------------+
//| 1. Objeto simple con un string |
//+------------------------------------------------------------------+
class CTestBuilder_SimpleObjString : public ICiTest
{
public:
inline string Name() const override final { return "Builder_SimpleObj_String"; }
inline int Importance() const override final { return 95; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("symbol").ValS("EURUSD").EndObj();
string out = b.Build();
if(out != "{\"symbol\":\"EURUSD\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 2. Objeto con multiples tipos (int, double, bool, null) |
//+------------------------------------------------------------------+
class CTestBuilder_MultiTypeObj : public ICiTest
{
public:
inline string Name() const override final { return "Builder_MultiTypeObj"; }
inline int Importance() const override final { return 95; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("id").Val(7);
b.Key("price").Val(1.5, 2);
b.Key("active").Val(true);
b.Key("nothing").Null();
b.EndObj();
string out = b.Build();
if(out != "{\"id\":7,\"price\":1.50,\"active\":true,\"nothing\":null}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 3. Array simple de enteros |
//+------------------------------------------------------------------+
class CTestBuilder_ArrayInts : public ICiTest
{
public:
inline string Name() const override final { return "Builder_Array_Ints"; }
inline int Importance() const override final { return 90; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Arr().Val(1).Val(2).Val(3).EndArr();
string out = b.Build();
if(out != "[1,2,3]")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 4. Array vacio |
//+------------------------------------------------------------------+
class CTestBuilder_EmptyArray : public ICiTest
{
public:
inline string Name() const override final { return "Builder_EmptyArray"; }
inline int Importance() const override final { return 75; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Arr().EndArr();
string out = b.Build();
if(out != "[]")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 5. Objeto vacio |
//+------------------------------------------------------------------+
class CTestBuilder_EmptyObj : public ICiTest
{
public:
inline string Name() const override final { return "Builder_EmptyObj"; }
inline int Importance() const override final { return 75; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj().EndObj();
string out = b.Build();
if(out != "{}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 6. Objeto anidado dentro de objeto |
//+------------------------------------------------------------------+
class CTestBuilder_NestedObjInObj : public ICiTest
{
public:
inline string Name() const override final { return "Builder_NestedObjInObj"; }
inline int Importance() const override final { return 90; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("meta").Obj();
b.Key("id").Val(1);
b.EndObj();
b.EndObj();
string out = b.Build();
if(out != "{\"meta\":{\"id\":1}}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 7. Array de objetos |
//+------------------------------------------------------------------+
class CTestBuilder_ArrayOfObjects : public ICiTest
{
public:
inline string Name() const override final { return "Builder_ArrayOfObjects"; }
inline int Importance() const override final { return 90; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Arr();
b.Obj();
b.Key("id").Val(1).EndObj();
b.Obj();
b.Key("id").Val(2).EndObj();
b.EndArr();
string out = b.Build();
if(out != "[{\"id\":1},{\"id\":2}]")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 8. Array anidado (array de arrays) |
//+------------------------------------------------------------------+
class CTestBuilder_NestedArrays : public ICiTest
{
public:
inline string Name() const override final { return "Builder_NestedArrays"; }
inline int Importance() const override final { return 85; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Arr();
b.Arr().Val(1).Val(2).EndArr();
b.Arr().Val(3).Val(4).EndArr();
b.EndArr();
string out = b.Build();
if(out != "[[1,2],[3,4]]")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 9. ValS escapa comillas y backslash correctamente (simple) |
//+------------------------------------------------------------------+
class CTestBuilder_EscapeQuotesBackslash : public ICiTest
{
public:
inline string Name() const override final { return "Builder_ValS_EscapeQuotesBackslash"; }
inline int Importance() const override final { return 90; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("k").ValS("say \"hi\"\\end").EndObj();
string out = b.Build();
if(out != "{\"k\":\"say \\\"hi\\\"\\\\end\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 10. ValS escapa newline y tab |
//+------------------------------------------------------------------+
class CTestBuilder_EscapeNewlineTab : public ICiTest
{
public:
inline string Name() const override final { return "Builder_ValS_EscapeNewlineTab"; }
inline int Importance() const override final { return 85; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("k").ValS("line1\nline2\ttabbed").EndObj();
string out = b.Build();
if(out != "{\"k\":\"line1\\nline2\\ttabbed\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 11. ValS escapa unicode > 0xFF como \uXXXX |
//+------------------------------------------------------------------+
class CTestBuilder_EscapeUnicode : public ICiTest
{
public:
inline string Name() const override final { return "Builder_ValS_EscapeUnicode"; }
inline int Importance() const override final { return 80; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
// U+2514 BOX DRAWINGS LIGHT UP AND RIGHT ("└")
b.Obj();
b.Key("k").ValS("").EndObj();
string out = b.Build();
if(out != "{\"k\":\"\\u2514\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 12. Val(double) respeta el numero de decimales (padding) |
//+------------------------------------------------------------------+
class CTestBuilder_DoublePadding : public ICiTest
{
public:
inline string Name() const override final { return "Builder_ValDouble_DigitsPadding"; }
inline int Importance() const override final { return 85; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("v").Val(20.0, 8).EndObj();
string out = b.Build();
if(out != "{\"v\":20.00000000}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 13. Val(double) negativo |
//+------------------------------------------------------------------+
class CTestBuilder_DoubleNegative : public ICiTest
{
public:
inline string Name() const override final { return "Builder_ValDouble_Negative"; }
inline int Importance() const override final { return 80; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("v").Val(-1.5, 2).EndObj();
string out = b.Build();
if(out != "{\"v\":-1.50}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 14. Val(long) con valor grande |
//+------------------------------------------------------------------+
class CTestBuilder_LongBigValue : public ICiTest
{
public:
inline string Name() const override final { return "Builder_ValLong_BigValue"; }
inline int Importance() const override final { return 75; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("v").Val((long)9223372036854775).EndObj();
string out = b.Build();
if(out != "{\"v\":9223372036854775}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 15. Val(int) con cero y negativo |
//+------------------------------------------------------------------+
class CTestBuilder_IntZeroAndNegative : public ICiTest
{
public:
inline string Name() const override final { return "Builder_ValInt_ZeroAndNegative"; }
inline int Importance() const override final { return 75; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Arr().Val(0).Val(-42).EndArr();
string out = b.Build();
if(out != "[0,-42]")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 16. KeyWV / ValSWV -> sin validacion/escape (raw passthrough) |
//+------------------------------------------------------------------+
class CTestBuilder_RawNoValidation : public ICiTest
{
public:
inline string Name() const override final { return "Builder_KeyWV_ValSWV_RawPassthrough"; }
inline int Importance() const override final { return 70; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.KeyWV("raw_key").ValSWV("raw_value").EndObj();
string out = b.Build();
// Sin escape: se inserta literal, sin procesar caracteres especiales
if(out != "{\"raw_key\":\"raw_value\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 17. ValidJson inserta fragmento crudo sin escapar (JSON valido) |
//+------------------------------------------------------------------+
class CTestBuilder_ValidJsonFragment : public ICiTest
{
public:
inline string Name() const override final { return "Builder_ValidJson_RawFragment"; }
inline int Importance() const override final { return 80; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("data").ValidJson("{\"nested\":true}").EndObj();
string out = b.Build();
if(out != "{\"data\":{\"nested\":true}}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 18. Clear() reinicia el builder correctamente |
//+------------------------------------------------------------------+
class CTestBuilder_ClearResets : public ICiTest
{
public:
inline string Name() const override final { return "Builder_Clear_Resets"; }
inline int Importance() const override final { return 70; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder b;
b.Obj();
b.Key("a").Val(1).EndObj();
b.Clear();
b.Obj();
b.Key("b").Val(2).EndObj();
string out = b.Build();
if(out != "{\"b\":2}")
{
msg = StringFormat("obtenido '%s' (deberia no contener residuo de 'a')", out);
return false;
}
return true;
}
};
//======================================================================
// SECCION 2: CJsonBuilderStr (JSON doblemente escapado)
//======================================================================
//+------------------------------------------------------------------+
//| 19. Objeto simple con doble escape de comillas |
//+------------------------------------------------------------------+
class CTestBuilderStr_SimpleObjDoubleEscaped : public ICiTest
{
public:
inline string Name() const override final { return "BuilderStr_SimpleObj_DoubleEscaped"; }
inline int Importance() const override final { return 95; }
bool Run(string &msg) override final
{
TSN::CJsonBuilderStr b;
b.Obj();
b.Key("symbol").ValS("EURUSD").EndObj();
string out = b.Build();
// Cada '"' estructural se emite como \" (escapado una vez extra respecto al builder simple)
if(out != "{\\\"symbol\\\":\\\"EURUSD\\\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 20. ValS escapa backslash+comilla con triple barra |
//+------------------------------------------------------------------+
class CTestBuilderStr_EscapeQuoteTriple : public ICiTest
{
public:
inline string Name() const override final { return "BuilderStr_ValS_EscapeQuoteTriple"; }
inline int Importance() const override final { return 90; }
bool Run(string &msg) override final
{
TSN::CJsonBuilderStr b;
b.Obj();
b.Key("k").ValS("a\"b").EndObj();
string out = b.Build();
// La comilla dentro del valor -> \\\" (triple barra + comilla)
if(out != "{\\\"k\\\":\\\"a\\\\\\\"b\\\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 21. ValS escapa newline con doble barra (\\n) |
//+------------------------------------------------------------------+
class CTestBuilderStr_EscapeNewlineDouble : public ICiTest
{
public:
inline string Name() const override final { return "BuilderStr_ValS_EscapeNewlineDouble"; }
inline int Importance() const override final { return 85; }
bool Run(string &msg) override final
{
TSN::CJsonBuilderStr b;
b.Obj();
b.Key("k").ValS("a\nb").EndObj();
string out = b.Build();
if(out != "{\\\"k\\\":\\\"a\\\\nb\\\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 22. ValS escapa unicode > 0xFF con doble barra (\\uXXXX) |
//+------------------------------------------------------------------+
class CTestBuilderStr_EscapeUnicodeDouble : public ICiTest
{
public:
inline string Name() const override final { return "BuilderStr_ValS_EscapeUnicodeDouble"; }
inline int Importance() const override final { return 80; }
bool Run(string &msg) override final
{
TSN::CJsonBuilderStr b;
b.Obj();
b.Key("k").ValS("").EndObj();
string out = b.Build();
if(out != "{\\\"k\\\":\\\"\\\\u2514\\\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 23. Objeto anidado con CJsonBuilderStr |
//+------------------------------------------------------------------+
class CTestBuilderStr_NestedObj : public ICiTest
{
public:
inline string Name() const override final { return "BuilderStr_NestedObj"; }
inline int Importance() const override final { return 85; }
bool Run(string &msg) override final
{
TSN::CJsonBuilderStr b;
b.Obj();
b.Key("meta").Obj();
b.Key("id").Val(1);
b.EndObj();
b.EndObj();
string out = b.Build();
if(out != "{\\\"meta\\\":{\\\"id\\\":1}}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 24. Array con CJsonBuilderStr y valores mixtos |
//+------------------------------------------------------------------+
class CTestBuilderStr_ArrayMixedValues : public ICiTest
{
public:
inline string Name() const override final { return "BuilderStr_Array_MixedValues"; }
inline int Importance() const override final { return 80; }
bool Run(string &msg) override final
{
TSN::CJsonBuilderStr b;
b.Arr();
b.Val(1).Val(true).Null();
b.ValS("x").EndArr();
string out = b.Build();
if(out != "[1,true,null,\\\"x\\\"]")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 25. ValSWV en CJsonBuilderStr -> sigue envolviendo en \" sin |
//| escapar el contenido interno |
//+------------------------------------------------------------------+
class CTestBuilderStr_ValSWVRaw : public ICiTest
{
public:
inline string Name() const override final { return "BuilderStr_ValSWV_RawPassthrough"; }
inline int Importance() const override final { return 65; }
bool Run(string &msg) override final
{
TSN::CJsonBuilderStr b;
b.Obj();
b.Key("k").ValSWV("raw").EndObj();
string out = b.Build();
if(out != "{\\\"k\\\":\\\"raw\\\"}")
{
msg = StringFormat("obtenido '%s'", out);
return false;
}
return true;
}
};
//+------------------------------------------------------------------+
//| 26. Comparativa cruzada: mismo contenido, distinto nivel de |
//| escape entre CJsonBuilder y CJsonBuilderStr |
//+------------------------------------------------------------------+
class CTestBuilder_CrossCompareEscapeLevels : public ICiTest
{
public:
inline string Name() const override final { return "Builder_CrossCompare_EscapeLevels"; }
inline int Importance() const override final { return 75; }
bool Run(string &msg) override final
{
TSN::CJsonBuilder simple;
TSN::CJsonBuilderStr doubled;
simple.Obj();
simple.Key("k").ValS("v").EndObj();
doubled.Obj();
doubled.Key("k").ValS("v").EndObj();
string out_simple = simple.Build();
string out_doubled = doubled.Build();
if(out_simple != "{\"k\":\"v\"}")
{
msg = StringFormat("CJsonBuilder produjo '%s', esperado JSON valido simple", out_simple);
return false;
}
if(out_doubled != "{\\\"k\\\":\\\"v\\\"}")
{
msg = StringFormat("CJsonBuilderStr produjo '%s', esperado doble escape", out_doubled);
return false;
}
if(out_simple == out_doubled)
{
msg = "ambos builders produjeron el mismo string, se esperaba escape distinto";
return false;
}
return true;
}
};
}
//+------------------------------------------------------------------+
#endif // JSONPARSERBYLEO_WF_INC_BUILDERTEST_MQH