//+------------------------------------------------------------------+ //| UnitTests.mq5 | //| Unit Tests for Package logger | //| Organization: douglasrechia | //+------------------------------------------------------------------+ #include "../knitpkg/build/BuildInfo.mqh" #property copyright "Douglas Rechia" #property link "https://knitpkg.dev" #property description "" #property description "Version: " + MANIFEST_VERSION #property description "" #property description "Description: Unit tests for package logger" #property description "Organization: douglasrechia" #property description "Author: Douglas Rechia" #property description "License: MIT" #property description "" #property description "Powered by KnitPkg for MetaTrader" #property description "https://knitpkg.dev" #include "../knitpkg/include/douglasrechia/logger/Logger.mqh" #include "../knitpkg/include/douglasrechia/logger/handlers/PrintLogger.mqh" #include "../knitpkg/include/douglasrechia/logger/handlers/FileLogger.mqh" //+------------------------------------------------------------------+ //| Asserts log file content matches expected patterns | //+------------------------------------------------------------------+ bool AssertLogContent(string filePath, string &expectedPatterns[], string testName) { int fileHandle = FileOpen(filePath, FILE_READ | FILE_TXT | FILE_ANSI); if(fileHandle == INVALID_HANDLE) { PrintFormat("%s: Failed to open log file for verification", testName); return false; } int patternIndex = 0; bool allMatch = true; int lineNumber = 0; while(!FileIsEnding(fileHandle)) { string line = FileReadString(fileHandle); if(line == "") continue; lineNumber++; if(patternIndex >= ArraySize(expectedPatterns)) { PrintFormat("%s: More lines than expected in log file", testName); allMatch = false; break; } // Check if line contains expected pattern if(StringFind(line, expectedPatterns[patternIndex]) < 0) { PrintFormat("%s: Line %d mismatch. Expected to contain: '%s', Got: '%s'", testName, lineNumber, expectedPatterns[patternIndex], line); allMatch = false; } patternIndex++; if(StringFind(line, expectedPatterns[patternIndex]) < 0) { PrintFormat("%s: Line %d mismatch. Expected to contain: '%s', Got: '%s'", testName, lineNumber, expectedPatterns[patternIndex], line); allMatch = false; } patternIndex++; } FileClose(fileHandle); if(patternIndex < ArraySize(expectedPatterns)) { PrintFormat("%s: Expected %d lines, got %d", testName, ArraySize(expectedPatterns), patternIndex); allMatch = false; } return allMatch; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool TestAll() { douglasrechia::Logger* g_log = new douglasrechia::Logger(__FILE__, douglasrechia::LOG_LEVEL_DEBUG); g_log.AddHandler(new douglasrechia::FileLogger("logs/log_test_all.log", true, false)); g_log.Info("TestAll", "info"); g_log.Debug("TestAll", "debug"); g_log.Warn("TestAll", "warn"); g_log.Error("TestAll", "error"); g_log.Info("TestAll info"); g_log.Debug("TestAll debug"); g_log.Warn("TestAll warn"); g_log.Error("TestAll error"); delete g_log; string expectedPatterns[] = { "[UnitTests.mq5][INFO ] ", "| TestAll | info", "[UnitTests.mq5][DEBUG] ", "| TestAll | debug", "[UnitTests.mq5][WARN ] ", "| TestAll | warn", "[UnitTests.mq5][ERROR] ", "| TestAll | error", "[UnitTests.mq5][INFO ] ", "| TestAll info", "[UnitTests.mq5][DEBUG] ", "| TestAll debug", "[UnitTests.mq5][WARN ] ", "| TestAll warn", "[UnitTests.mq5][ERROR] ", "| TestAll error" }; return AssertLogContent("logs/log_test_all.log", expectedPatterns, "TestAll"); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool TestInfo() { douglasrechia::Logger* g_log = new douglasrechia::Logger(__FILE__, douglasrechia::LOG_LEVEL_INFO); g_log.AddHandler(new douglasrechia::FileLogger("logs/log_test_info.log", true, false)); g_log.Info("TestInfo", "info"); g_log.Debug("TestInfo", "debug"); g_log.Warn("TestInfo", "warn"); g_log.Error("TestInfo", "error"); g_log.Info("TestInfo info"); g_log.Debug("TestInfo debug"); g_log.Warn("TestInfo warn"); g_log.Error("TestInfo error"); delete g_log; string expectedPatterns[] = { "[UnitTests.mq5][INFO ] ", "| TestInfo | info", "[UnitTests.mq5][WARN ] ", "| TestInfo | warn", "[UnitTests.mq5][ERROR] ", "| TestInfo | error", "[UnitTests.mq5][INFO ] ", "| TestInfo info", "[UnitTests.mq5][WARN ] ", "| TestInfo warn", "[UnitTests.mq5][ERROR] ", "| TestInfo error" }; return AssertLogContent("logs/log_test_info.log", expectedPatterns, "TestInfo"); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool TestWarn() { douglasrechia::Logger* g_log = new douglasrechia::Logger(__FILE__, douglasrechia::LOG_LEVEL_WARN); g_log.AddHandler(new douglasrechia::FileLogger("logs/log_test_warn.log", true, false)); g_log.Info("TestWarn", "info"); g_log.Debug("TestWarn", "debug"); g_log.Warn("TestWarn", "warn"); g_log.Error("TestWarn", "error"); g_log.Info("TestWarn info"); g_log.Debug("TestWarn debug"); g_log.Warn("TestWarn warn"); g_log.Error("TestWarn error"); delete g_log; string expectedPatterns[] = { "[UnitTests.mq5][WARN ] ", "| TestWarn | warn", "[UnitTests.mq5][ERROR] ", "| TestWarn | error", "[UnitTests.mq5][WARN ] ", "| TestWarn warn", "[UnitTests.mq5][ERROR] ", "| TestWarn error" }; return AssertLogContent("logs/log_test_warn.log", expectedPatterns, "TestWarn"); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool TestError() { douglasrechia::Logger* g_log = new douglasrechia::Logger(__FILE__, douglasrechia::LOG_LEVEL_ERROR); g_log.AddHandler(new douglasrechia::FileLogger("logs/log_test_error.log", true, false)); g_log.Info("TestError", "info"); g_log.Debug("TestError", "debug"); g_log.Warn("TestError", "warn"); g_log.Error("TestError", "error"); g_log.Info("TestError info"); g_log.Debug("TestError debug"); g_log.Warn("TestError warn"); g_log.Error("TestError error"); delete g_log; string expectedPatterns[] = { "[UnitTests.mq5][ERROR] ", "| TestError | error", "[UnitTests.mq5][ERROR] ", "| TestError error" }; return AssertLogContent("logs/log_test_error.log", expectedPatterns, "TestError"); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool TestErrorAppend() { douglasrechia::Logger* g_log = new douglasrechia::Logger(__FILE__, douglasrechia::LOG_LEVEL_ERROR); g_log.AddHandler(new douglasrechia::FileLogger("logs/log_test_error.log", true, false)); g_log.Info("TestError", "info"); g_log.Debug("TestError", "debug"); g_log.Warn("TestError", "warn"); g_log.Error("TestError", "error"); g_log.Info("TestError info"); g_log.Debug("TestError debug"); g_log.Warn("TestError warn"); g_log.Error("TestError error"); delete g_log; string expectedPatterns1[] = { "[UnitTests.mq5][ERROR] ", "| TestError | error", "[UnitTests.mq5][ERROR] ", "| TestError error" }; bool check1 = AssertLogContent("logs/log_test_error.log", expectedPatterns1, "TestError"); g_log = new douglasrechia::Logger(__FILE__, douglasrechia::LOG_LEVEL_ERROR); g_log.AddHandler(new douglasrechia::FileLogger("logs/log_test_error.log", true, true)); g_log.Info("TestError", "info"); g_log.Debug("TestError", "debug"); g_log.Warn("TestError", "warn"); g_log.Error("TestError", "error"); g_log.Info("TestError info"); g_log.Debug("TestError debug"); g_log.Warn("TestError warn"); g_log.Error("TestError error"); delete g_log; string expectedPatterns2[] = { "[UnitTests.mq5][ERROR] ", "| TestError | error", "[UnitTests.mq5][ERROR] ", "| TestError error", "[UnitTests.mq5][ERROR] ", "| TestError | error", "[UnitTests.mq5][ERROR] ", "| TestError error" }; bool check2 = AssertLogContent("logs/log_test_error.log", expectedPatterns2, "TestError"); return check1 && check2; } //+------------------------------------------------------------------+ //| DoTests | //+------------------------------------------------------------------+ void DoTests(int &testsPerformed,int &testsPassed) { string testName=""; //--- TestAll testsPerformed++; testName="TestAll"; if(TestAll()) { testsPassed++; PrintFormat("%s pass",testName); } else PrintFormat("%s failed",testName); //--- TestInfo testsPerformed++; testName="TestInfo"; if(TestInfo()) { testsPassed++; PrintFormat("%s pass",testName); } else PrintFormat("%s failed",testName); //--- TestWarn testsPerformed++; testName="TestWarn"; if(TestWarn()) { testsPassed++; PrintFormat("%s pass",testName); } else PrintFormat("%s failed",testName); //--- TestError testsPerformed++; testName="TestError"; if(TestError()) { testsPassed++; PrintFormat("%s pass",testName); } else PrintFormat("%s failed",testName); //--- TestErrorAppend testsPerformed++; testName="TestErrorAppend"; if(TestErrorAppend()) { testsPassed++; PrintFormat("%s pass",testName); } else PrintFormat("%s failed",testName); //--- // Add more tests here as needed } //+------------------------------------------------------------------+ //| UnitTests() | //+------------------------------------------------------------------+ void UnitTests(const string packageName) { PrintFormat("Unit tests for Package %s\n",packageName); //--- initial values int testsPerformed=0; int testsPassed=0; //--- test distributions DoTests(testsPerformed,testsPassed); //--- print statistics PrintFormat("\n%d of %d passed",testsPassed,testsPerformed); } //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { UnitTests("logger"); } //+------------------------------------------------------------------+