2026-03-09 15:41:24 -05:00
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| Main.mqh |
|
|
|
|
|
//| Copyright 2026, MetaQuotes Ltd. |
|
|
|
|
|
//| https://www.mql5.com |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#property copyright "Copyright 2026, MetaQuotes Ltd."
|
|
|
|
|
#property link "https://www.mql5.com"
|
|
|
|
|
#property strict
|
|
|
|
|
|
|
|
|
|
#ifndef MQLCYBYLEO_SRC_TESTER_BASE_MQH
|
|
|
|
|
#define MQLCYBYLEO_SRC_TESTER_BASE_MQH
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
#include "Defines.mqh"
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
class CMqlCIClassCi : public CLoggerBase
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
//--- Varialbes parametro
|
2026-03-09 16:46:30 -05:00
|
|
|
string m_filename_compiled_log; // Nombre del archivo donde se pondra en caso de fallo de compilacion los logs(RUTA RELATIVA)
|
2026-03-09 15:41:24 -05:00
|
|
|
// Nombre del archivo donde estara el resultado de la compilacion 0 \ 1.. si es 0 = flase (Se ubicara en mql5\\files\\...)
|
|
|
|
|
// El py lo debera de leeer y marcar como "invalido" el action
|
|
|
|
|
// La ruta a este archivo no neceista path completo sobr eel nomrbe (Se ubicara en mql5\\files\\...)
|
|
|
|
|
string m_file_name_res;
|
|
|
|
|
string m_file_name_out_json; // Nombre del archivo json (Se ubicara en mql5\\files\\...)
|
|
|
|
|
string m_file_name_read_py; // Archivo de "check para py" este lo leera y sabra uqe ya es hora de emepzar los check
|
|
|
|
|
uint m_max_timeout_ms_compile; // Maximo tiempo de espera para compilar
|
2026-03-11 17:12:08 -05:00
|
|
|
int m_timeout_per_test_sec; // Maximo tiempo a esperar un solo test (segundos)
|
2026-03-09 15:41:24 -05:00
|
|
|
|
|
|
|
|
// Expertos
|
|
|
|
|
// Path relativo a Experts
|
|
|
|
|
TestExpertCi m_experts_test[];
|
|
|
|
|
int m_experts_test_size;
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
TestingCiLogs m_event_log[]; // Registro de eventos de la ejecucion (tests + eventos del sistema)
|
|
|
|
|
long m_last_chart_id; // ID del ultimo chart abierto para el test
|
|
|
|
|
int m_real_test_passed_size; // Contador de tests que pasaron exitosamente
|
|
|
|
|
int m_event_log_size; // Tamanio actual del array m_event_log
|
|
|
|
|
datetime m_ex_start_data; // Fecha/hora de inicio del test actual
|
|
|
|
|
datetime m_ex_end_date; // Fecha/hora de fin del test actual
|
|
|
|
|
int m_current_idx_expert; // Indice del expert que se esta ejecutando actualmente
|
|
|
|
|
|
2026-03-11 17:12:08 -05:00
|
|
|
//---
|
|
|
|
|
bool m_in_test;
|
|
|
|
|
|
2026-03-09 15:41:24 -05:00
|
|
|
//--- Funcoines
|
|
|
|
|
void BuildTestJson();
|
|
|
|
|
__forceinline void ExtractTextLogFile(const int idx);
|
|
|
|
|
bool Compile(); // Compila todos los archivos
|
|
|
|
|
void RunTest(); // Corre los test
|
|
|
|
|
bool WriteInFileRes(int8_t flags); // Escribe el archivo binario las badenras de exito
|
|
|
|
|
void RunPyAndFinish();
|
|
|
|
|
|
|
|
|
|
public:
|
2026-03-11 17:12:08 -05:00
|
|
|
CMqlCIClassCi(void): m_in_test(false), m_last_chart_id(-1) {}
|
2026-03-09 15:41:24 -05:00
|
|
|
~CMqlCIClassCi(void) {}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-03-11 17:12:08 -05:00
|
|
|
bool Init(const MqlCiConfigCompiler& config, const TestExpertCi& arr_experts[]);
|
2026-03-09 15:41:24 -05:00
|
|
|
|
|
|
|
|
//---
|
2026-03-09 16:06:27 -05:00
|
|
|
bool First();
|
2026-03-09 15:41:24 -05:00
|
|
|
void ChartEvent(const int32_t id, const long& lparam, const double& dparam, const string& sparam);
|
|
|
|
|
bool Execute();
|
2026-03-11 17:12:08 -05:00
|
|
|
void TimerEvent(); // Check
|
2026-03-09 15:41:24 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-11 17:12:08 -05:00
|
|
|
bool CMqlCIClassCi::Init(const MqlCiConfigCompiler &config, const TestExpertCi& arr_experts[])
|
2026-03-09 15:41:24 -05:00
|
|
|
{
|
2026-03-09 18:42:31 -05:00
|
|
|
//--- ESperamos carga
|
2026-03-10 06:38:46 -05:00
|
|
|
const long chart_id = ::ChartID();
|
|
|
|
|
::Sleep(500); // Primera espera inicial (no tatno por que ya es el primero en cargar)
|
2026-03-09 21:16:43 -05:00
|
|
|
while(true)
|
|
|
|
|
{
|
|
|
|
|
// COmprobacion de barras
|
2026-03-10 06:38:46 -05:00
|
|
|
if(::ChartGetInteger(chart_id, CHART_VISIBLE_BARS) > 0 && ::SeriesInfoInteger(_Symbol, _Period, SERIES_SYNCHRONIZED))
|
2026-03-09 21:16:43 -05:00
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Espera para la siguiente iteracion
|
|
|
|
|
Sleep(1000);
|
|
|
|
|
}
|
2026-03-09 18:42:31 -05:00
|
|
|
|
|
|
|
|
//--- CErramos todos los graficos que pudieran haber menos el actual
|
2026-03-09 21:16:43 -05:00
|
|
|
CloseAllChartsExceptChart(chart_id);
|
2026-03-09 18:42:31 -05:00
|
|
|
|
2026-03-09 15:41:24 -05:00
|
|
|
//---
|
2026-03-10 15:27:52 -05:00
|
|
|
m_filename_compiled_log = TERMINAL_MT5_ROOT + "Files\\" + config.filename_compiled_log;
|
2026-03-09 15:41:24 -05:00
|
|
|
m_file_name_res = config.file_name_res;
|
|
|
|
|
m_file_name_out_json = config.file_name_out_json;
|
|
|
|
|
m_file_name_read_py = config.file_name_read_py;
|
|
|
|
|
m_max_timeout_ms_compile = config.max_timeout_ms_compile;
|
2026-03-11 17:12:08 -05:00
|
|
|
m_timeout_per_test_sec = config.max_timeout_esecution_per_test;
|
2026-03-09 15:41:24 -05:00
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_experts_test_size = ArrayCopyCts(m_experts_test, arr_experts);
|
|
|
|
|
if(m_experts_test_size < 1)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
::ArrayResize(m_event_log, m_event_log_size + 1);
|
|
|
|
|
m_event_log[m_event_log_size].label = "Iniciciion test - Experts size";
|
|
|
|
|
m_event_log[m_event_log_size].res = CYBYLEO_CODE_ERROR;
|
|
|
|
|
m_event_log[m_event_log_size].log_txt = ::StringFormat("Numero de expertos es invalido=%d", m_experts_test_size);
|
|
|
|
|
LogError(m_event_log[m_event_log_size++].log_txt, FUNCION_ACTUAL);
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
WriteInFileRes(0);
|
|
|
|
|
RunPyAndFinish();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
m_event_log_size = ::ArrayResize(m_event_log, 0);
|
|
|
|
|
m_real_test_passed_size = 0;
|
|
|
|
|
m_current_idx_expert = 0;
|
|
|
|
|
m_last_chart_id = -1;
|
2026-03-11 17:12:08 -05:00
|
|
|
m_in_test = false;
|
2026-03-09 15:41:24 -05:00
|
|
|
|
2026-03-10 06:38:46 -05:00
|
|
|
//--- Print final}
|
|
|
|
|
LogInfo("Iniciado CI - CD", FUNCION_ACTUAL);
|
2026-03-09 18:03:33 -05:00
|
|
|
|
2026-03-09 15:41:24 -05:00
|
|
|
//---
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CMqlCIClassCi::Compile(void)
|
|
|
|
|
{
|
|
|
|
|
for(int i = 0; i < m_experts_test_size; i++)
|
|
|
|
|
{
|
|
|
|
|
static string log_path;
|
|
|
|
|
|
|
|
|
|
// EN caso la compilacion falle tenemos que escribir en el archivo compile el log del archivo que fallo y cerrar el terminal y abrir el py
|
2026-03-10 15:27:52 -05:00
|
|
|
if(!CompileFileWithLogFile(TERMINAL_MT5_ROOT + m_experts_test[i].expert_mq5_path, log_path, m_max_timeout_ms_compile))
|
2026-03-09 15:41:24 -05:00
|
|
|
{
|
2026-03-09 16:06:27 -05:00
|
|
|
LogError(::StringFormat("Fallo al compilar:\n%s", m_experts_test[i].expert_mq5_path), FUNCION_ACTUAL);
|
|
|
|
|
LogError(::StringFormat("Log path:\n%s", log_path), FUNCION_ACTUAL);
|
2026-03-09 15:41:24 -05:00
|
|
|
MTTESTER::FileMove(log_path, m_filename_compiled_log, true); // Movemos el archivo con el nombre del path
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
bool CMqlCIClassCi::WriteInFileRes(int8_t flags)
|
|
|
|
|
{
|
|
|
|
|
const int file_handle = ::FileOpen(m_file_name_res, FILE_BIN | FILE_WRITE);
|
|
|
|
|
if(file_handle == INVALID_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
// En caso falle no se creara asi que el py tiene dos cosas... digo 3
|
|
|
|
|
// Si [No se puede cargar el archivo por esta corrupta | el archivo tiene valor 0 | el archivo no existe] entonces fallo la compilacion
|
|
|
|
|
|
|
|
|
|
LogError(::StringFormat("Fallo al crear el archivo = %s", m_file_name_res), FUNCION_ACTUAL);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
::FileWriteInteger(file_handle, flags, sizeof(int8_t));
|
|
|
|
|
::FileClose(file_handle);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CMqlCIClassCi::RunTest(void)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
m_ex_start_data = ::TimeLocal();
|
|
|
|
|
|
2026-03-11 17:12:08 -05:00
|
|
|
//--- chekeamos el tamaño de parametros en caso sea menor a 2 entonces resize
|
|
|
|
|
const int size_params = ArraySize(m_experts_test[m_current_idx_expert].params);
|
|
|
|
|
if(size_params < 2)
|
|
|
|
|
ArrayResize(m_experts_test[m_current_idx_expert].params, 2); // Minimo un tamaño de dos siempre
|
|
|
|
|
|
|
|
|
|
//--- sobreesciemos los dos primeros parametros
|
|
|
|
|
m_experts_test[m_current_idx_expert].params[0].type = TYPE_STRING;
|
|
|
|
|
m_experts_test[m_current_idx_expert].params[0].string_value = m_experts_test[m_current_idx_expert].expert_ex5_name;
|
|
|
|
|
m_experts_test[m_current_idx_expert].params[1].type = TYPE_LONG;
|
|
|
|
|
m_experts_test[m_current_idx_expert].params[1].integer_value = ChartID();
|
2026-03-09 15:41:24 -05:00
|
|
|
|
2026-03-11 17:12:08 -05:00
|
|
|
//--- abrimos un nuevo graficos
|
2026-03-09 15:41:24 -05:00
|
|
|
m_last_chart_id = ::ChartOpen(m_experts_test[m_current_idx_expert].symbol, m_experts_test[m_current_idx_expert].timeframe);
|
2026-03-11 17:12:08 -05:00
|
|
|
Sleep(750); // Primera espera iniicla
|
2026-03-09 21:16:43 -05:00
|
|
|
|
2026-03-11 17:12:08 -05:00
|
|
|
//--- Check sincronizacion del grafico
|
2026-03-09 21:16:43 -05:00
|
|
|
while(true)
|
|
|
|
|
{
|
|
|
|
|
// COmprobacion de barras | Verificamos sincronizacion
|
|
|
|
|
if(ChartGetInteger(m_last_chart_id, CHART_VISIBLE_BARS) > 0 &&
|
|
|
|
|
SeriesInfoInteger(m_experts_test[m_current_idx_expert].symbol, m_experts_test[m_current_idx_expert].timeframe, SERIES_SYNCHRONIZED))
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Espera para la siguiente iteracion
|
2026-03-11 17:12:08 -05:00
|
|
|
Sleep(750);
|
2026-03-09 21:16:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2026-03-09 18:03:33 -05:00
|
|
|
// Log informativo
|
|
|
|
|
LogInfo(StringFormat("Chart id = %I64d | EA a correr = %s", m_last_chart_id, m_experts_test[m_current_idx_expert].expert_ex5_name), FUNCION_ACTUAL);
|
|
|
|
|
|
2026-03-09 15:41:24 -05:00
|
|
|
//---
|
2026-03-11 17:12:08 -05:00
|
|
|
if(!EXPERT::Run(m_last_chart_id, m_experts_test[m_current_idx_expert].params))
|
2026-03-09 15:41:24 -05:00
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
m_ex_end_date = TimeLocal();
|
2026-03-09 18:03:33 -05:00
|
|
|
|
2026-03-09 15:41:24 -05:00
|
|
|
//---
|
|
|
|
|
::ArrayResize(m_event_log, m_event_log_size + 1);
|
|
|
|
|
ExtractTextLogFile(m_event_log_size); // Todos los logs generados antes de correr el ea y ahora los capturamos
|
|
|
|
|
m_event_log[m_event_log_size].label = "Ejecucion de EA";
|
|
|
|
|
m_event_log[m_event_log_size].res = CYBYLEO_CODE_ERROR;
|
|
|
|
|
|
|
|
|
|
//---
|
2026-03-09 15:53:38 -05:00
|
|
|
LogError("Fallo al correr EA", FUNCION_ACTUAL);
|
2026-03-09 15:41:24 -05:00
|
|
|
WriteInFileRes(CIBYLEO_FLAG_EXITO_COMPILACION); // Solo compilacion
|
|
|
|
|
RunPyAndFinish(); // Corremos el py y finalizamos
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-11 17:12:08 -05:00
|
|
|
m_in_test = true; // Empezamos
|
2026-03-09 15:41:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CMqlCIClassCi::ChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
|
|
|
|
|
{
|
|
|
|
|
if(id == CHARTEVENT_CUSTOM + CIBYLEO_ONEVENT_RES) // Ya recibimos
|
|
|
|
|
{
|
|
|
|
|
//---
|
2026-03-11 17:12:08 -05:00
|
|
|
m_in_test = false;
|
2026-03-09 15:41:24 -05:00
|
|
|
m_ex_end_date = ::TimeLocal(); // Fin de la ejeuccion
|
|
|
|
|
|
|
|
|
|
//--- Añadimos
|
|
|
|
|
::ArrayResize(m_event_log, m_event_log_size + 1);
|
|
|
|
|
m_event_log[m_event_log_size].label = sparam; // Por convecion nombre del ea
|
|
|
|
|
m_event_log[m_event_log_size].res = lparam; // Resultado
|
|
|
|
|
|
|
|
|
|
// Extraemos el log del journal
|
|
|
|
|
ExtractTextLogFile(m_event_log_size); // Extrae de start-end date
|
|
|
|
|
m_event_log_size++; // Aumentamos
|
|
|
|
|
|
|
|
|
|
// Cerramos el chart
|
|
|
|
|
::ChartClose(m_last_chart_id);
|
|
|
|
|
|
2026-03-10 06:38:46 -05:00
|
|
|
//---
|
2026-03-11 17:12:08 -05:00
|
|
|
LogInfo(StringFormat("Recibiendo info de %s, res = %d, puntuacion = %.2f", sparam, lparam, dparam), FUNCION_ACTUAL);
|
2026-03-10 06:38:46 -05:00
|
|
|
|
2026-03-09 15:41:24 -05:00
|
|
|
//---
|
|
|
|
|
if(lparam == CYBYLEO_CODE_ERROR) // Fallo al ejeuctar el test
|
|
|
|
|
{
|
|
|
|
|
// Para esto ya tendremos la tarea que fallo en el array asi qeu ya finalizariamos de una vez
|
|
|
|
|
WriteInFileRes(CIBYLEO_FLAG_EXITO_COMPILACION); // Solo se jeucto la compilacion bien el test no
|
|
|
|
|
RunPyAndFinish();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---
|
2026-03-11 17:12:08 -05:00
|
|
|
m_real_test_passed_size++; // ya pasamos esta tarea
|
|
|
|
|
m_current_idx_expert++; // siguiente test
|
2026-03-09 15:41:24 -05:00
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
if(m_current_idx_expert < m_experts_test_size)
|
|
|
|
|
{
|
|
|
|
|
RunTest(); // Repetimos el proceso
|
|
|
|
|
}
|
|
|
|
|
else // Fin de las tareas hora de prepara el py
|
|
|
|
|
{
|
|
|
|
|
WriteInFileRes(CIBYLEO_FLAG_EXITO_COMPILACION | CIBYLEO_FLAG_EXITO_TEST); // Todo se ejeucto bien
|
|
|
|
|
RunPyAndFinish();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
__forceinline void CMqlCIClassCi::ExtractTextLogFile(const int idx)
|
|
|
|
|
{
|
|
|
|
|
ExtractLogLinesAsStr(m_ex_start_data, m_ex_start_data, m_ex_end_date, m_event_log[m_event_log_size].log_txt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CMqlCIClassCi::RunPyAndFinish(void)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
::ArrayResize(m_event_log, m_event_log_size + 1);
|
|
|
|
|
m_event_log [m_event_log_size].label = "Ejecucion de python";
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
::ResetLastError();
|
|
|
|
|
const int fh = ::FileOpen(m_file_name_read_py, FILE_WRITE | FILE_BIN);
|
|
|
|
|
if(fh == INVALID_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
m_event_log[m_event_log_size].res = CYBYLEO_CODE_ERROR;
|
|
|
|
|
m_event_log[m_event_log_size].log_txt = ::StringFormat("Fallo al abrir el archivo = %s, ultimo error = %d", m_file_name_read_py, ::GetLastError());
|
|
|
|
|
LogError(m_event_log [m_event_log_size].log_txt, FUNCION_ACTUAL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FileWriteInteger(fh, 1, sizeof(int8_t));
|
|
|
|
|
FileClose(fh);
|
|
|
|
|
|
|
|
|
|
m_event_log[m_event_log_size].res = CYBYLEO_CODE_SUCCES;
|
|
|
|
|
m_event_log[m_event_log_size].log_txt = "";
|
|
|
|
|
}
|
|
|
|
|
m_event_log_size++; // Aumentamos
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
BuildTestJson();
|
|
|
|
|
|
|
|
|
|
//--- Cerramos el terminal
|
|
|
|
|
::TerminalClose(CYBYLEO_TERMINAL_RET_CODE_SUCCESS);
|
2026-03-09 15:53:38 -05:00
|
|
|
Sleep(10000);
|
2026-03-09 15:41:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CMqlCIClassCi::BuildTestJson(void)
|
|
|
|
|
{
|
|
|
|
|
//---
|
|
|
|
|
/*
|
|
|
|
|
{
|
|
|
|
|
"summary": {
|
|
|
|
|
"tests_passed": 10, // Numero de tareas que se paran (real task passed)
|
|
|
|
|
"tests_total": 12, // Numero de tareas totales
|
|
|
|
|
},
|
|
|
|
|
// Log events..
|
|
|
|
|
"log_events": [
|
|
|
|
|
{
|
|
|
|
|
"label": "Compilacion",
|
|
|
|
|
"result": 1,
|
|
|
|
|
"log_txt": "..."
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
//--- Construccion del json
|
|
|
|
|
CJsonBuilder builder;
|
|
|
|
|
builder.Obj();
|
|
|
|
|
|
|
|
|
|
builder.Key("summary").Obj();
|
|
|
|
|
builder.Key("tests_passed").Val(m_real_test_passed_size);
|
|
|
|
|
builder.Key("tests_total").Val(m_experts_test_size);
|
|
|
|
|
builder.EndObj();
|
|
|
|
|
|
|
|
|
|
builder.Key("log_events").Arr();
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < m_event_log_size; i++)
|
|
|
|
|
{
|
|
|
|
|
builder.Obj();
|
|
|
|
|
builder.Key("label").Val(m_event_log[i].label);
|
|
|
|
|
builder.Key("result").Val(m_event_log[i].res);
|
|
|
|
|
builder.Key("log_txt").Val(m_event_log[i].log_txt);
|
|
|
|
|
builder.EndObj();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder.EndArr();
|
|
|
|
|
builder.EndObj();
|
|
|
|
|
|
|
|
|
|
//---
|
|
|
|
|
::ResetLastError();
|
|
|
|
|
const int fh = ::FileOpen(m_file_name_out_json, FILE_WRITE | FILE_TXT);
|
|
|
|
|
if(fh == INVALID_HANDLE)
|
|
|
|
|
{
|
|
|
|
|
WriteInFileRes(CIBYLEO_FLAG_EXITO_COMPILACION); // Solo compilacion
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
::FileWrite(fh, builder.Build());
|
|
|
|
|
::FileClose(fh);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
2026-03-09 16:06:27 -05:00
|
|
|
bool CMqlCIClassCi::First(void)
|
2026-03-09 15:41:24 -05:00
|
|
|
{
|
|
|
|
|
//--- Primero trataremos de compilar todo en caso falle crearemos el archivo res y lo pondremos en 0 y cerramos el terminal
|
|
|
|
|
if(!Compile())
|
|
|
|
|
{
|
2026-03-09 16:06:27 -05:00
|
|
|
WriteInFileRes(0); // Escribimos en el archivo result fallo (nada no compilo)
|
|
|
|
|
RunPyAndFinish();
|
|
|
|
|
return false;
|
2026-03-09 15:41:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--- Si ya se han compilado todos loa rchivos con exito entonces pasamos al test
|
|
|
|
|
RunTest(); // Corremos el primer test (como minimo debera de haber un archivbo esto igualente se compurbea en Init)
|
2026-03-09 16:06:27 -05:00
|
|
|
return true;
|
2026-03-09 15:41:24 -05:00
|
|
|
}
|
|
|
|
|
|
2026-03-11 17:12:08 -05:00
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
//| |
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
void CMqlCIClassCi::TimerEvent(void)
|
|
|
|
|
{
|
|
|
|
|
if(!m_in_test)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const datetime current_time = TimeLocal();
|
|
|
|
|
if(current_time - m_ex_start_data > m_timeout_per_test_sec)
|
|
|
|
|
{
|
|
|
|
|
LogError(StringFormat("Timeout en test: %s", m_experts_test[m_current_idx_expert].expert_ex5_name), FUNCION_ACTUAL);
|
|
|
|
|
::ChartClose(m_last_chart_id);
|
|
|
|
|
WriteInFileRes(CIBYLEO_FLAG_EXITO_COMPILACION);
|
|
|
|
|
RunPyAndFinish();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-10 15:27:52 -05:00
|
|
|
//---
|
2026-03-09 15:41:24 -05:00
|
|
|
#endif // MQLCYBYLEO_SRC_TESTER_BASE_MQH
|
|
|
|
|
//+------------------------------------------------------------------+
|