From 7332c32d2de3b8182dbd1fbb0e48de32cf74ef72 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 31 Aug 2026 14:32:42 +0000 Subject: [PATCH] Add -o= output folder option for test artifacts. Tests wrote XML, YAML, HTML, and log files into the current directory. Add Test::Options::output (-o=, default "out"), create that folder before tests run, and write all artifacts there. Co-authored-by: igor.ermolaev --- src/Test/Test.cpp | 7 +++ src/Test/Test.h | 1 + src/Test/TestFile.cpp | 127 ++++++++++++++++----------------------- src/Test/TestLog.cpp | 8 +-- src/Test/TestOptions.h | 7 +++ src/Test/TestParam.cpp | 62 +++++++++---------- src/Test/TestParamV2.cpp | 12 ++-- src/Test/TestProp.cpp | 10 +-- src/Test/TestTable.cpp | 6 +- src/Test/TestYaml.cpp | 16 ++--- 10 files changed, 125 insertions(+), 131 deletions(-) diff --git a/src/Test/Test.cpp b/src/Test/Test.cpp index f4d39f1..4bba04b 100644 --- a/src/Test/Test.cpp +++ b/src/Test/Test.cpp @@ -136,12 +136,19 @@ namespace Test std::cout << " -e=test - exclude test filter." << std::endl << std::endl; std::cout << " -ll=1 - a log level." << std::endl << std::endl; std::cout << " -lf=test.log - a log file name." << std::endl << std::endl; + std::cout << " -o=out - an output folder for test artifacts." << std::endl << std::endl; std::cout << " -h or -? - to print this help message." << std::endl << std::endl; return 0; } int MakeTests(const Groups& groups, const Options& options) { + if (!Cpl::CreatePath(options.output)) + { + CPL_LOG_SS(Error, "Can't create output folder '" << options.output << "' !"); + return 1; + } + for (size_t t = 0; t < groups.size(); ++t) { const Group& group = groups[t]; diff --git a/src/Test/Test.h b/src/Test/Test.h index 1d972fd..02e4a66 100644 --- a/src/Test/Test.h +++ b/src/Test/Test.h @@ -27,6 +27,7 @@ #include "Cpl/Defs.h" #include "Cpl/Args.h" +#include "Cpl/File.h" #include "Cpl/Log.h" #include "Cpl/Performance.h" diff --git a/src/Test/TestFile.cpp b/src/Test/TestFile.cpp index d406ea5..fab7f64 100644 --- a/src/Test/TestFile.cpp +++ b/src/Test/TestFile.cpp @@ -45,7 +45,7 @@ extern "C" { /* Test file hierarchy FOR LINUX: -----/tmp/cpl/1 +----/cpl/1 /2 f 22222.js /zero0 @@ -67,79 +67,58 @@ namespace Test return a + '\\' + b; #endif } -#ifdef __linux__ - static const std::string testPath = "/tmp/cpl"; -#elif _WIN32 - static const std::string testPath = ".\\cpl"; -#endif + static std::string testPath; static const std::string testString = "123456789876543210"; - const static std::set all_folders = [](){ - std::set temp; - temp.insert(joinPath(testPath, "1")); - temp.insert(joinPath(testPath, "2")); - temp.insert(joinPath(testPath, "zero0")); - temp.insert(joinPath(testPath, joinPath("zero0", "test"))); - return temp; - }(); - - const static std::set not_exist_folders = [](){ - std::set temp; - temp.insert(joinPath(testPath, "999")); - temp.insert(joinPath(testPath, joinPath("zxcb", "999"))); - temp.insert(joinPath(testPath, "8")); - return temp; - }(); - - const static std::set existance_files = [](){ - std::set temp; - temp.insert(joinPath(testPath, "emptyFile.js")); - temp.insert(joinPath(testPath, joinPath("2", "22222.js"))); - temp.insert(joinPath(testPath, "notempty.txt")); - temp.insert(joinPath(testPath, "notemptyx2")); - return temp; - }(); - - const static std::set not_existance_files = [](){ - std::set temp; - temp.insert(joinPath(testPath, "bemptyFile.js")); - temp.insert(joinPath(testPath, joinPath("2", "b22222.js"))); - temp.insert(joinPath(testPath, "bnotempty.txt")); - temp.insert(joinPath(testPath, "bnotemptyx2")); - return temp; - }(); - - const static std::set empty_files = [](){ - std::set temp; - temp.insert(joinPath(testPath, "emptyFile.js")); - temp.insert(joinPath(testPath, joinPath("2", "22222.js"))); - return temp; - }(); - - //Every file have at least test string testString - const static std::vector> not_empty_files = [](){ - std::vector> temp; - temp.push_back({joinPath(testPath, "notempty.txt"), testString.size()}); - temp.push_back({joinPath(testPath, "notemptyx2"), 2*testString.size()}); - return temp; - }(); - - bool initializeTree() { -#ifdef __linux__ - auto p = mkdir(testPath.c_str(), 0777); - p |= mkdir(joinPath(testPath, "1").c_str(), 0777); - p |= mkdir(joinPath(testPath, "2").c_str(), 0777); - p |= mkdir(joinPath(testPath, "zero0").c_str(), 0777); - p |= mkdir(joinPath(testPath, joinPath("zero0", "test")).c_str(), 0777); -#elif _WIN32 - auto p = _mkdir(testPath.c_str()); - p |= _mkdir(joinPath(testPath, "1").c_str()); - p |= _mkdir(joinPath(testPath, "2").c_str()); - p |= _mkdir(joinPath(testPath, "zero0").c_str()); - p |= _mkdir(joinPath(testPath, joinPath("zero0", "test")).c_str()); -#endif + static std::set all_folders; + static std::set not_exist_folders; + static std::set existance_files; + static std::set not_existance_files; + static std::set empty_files; + static std::vector> not_empty_files; + + void initTestPaths(const Options& options) + { + testPath = options.OutputPath("cpl"); + + all_folders.clear(); + all_folders.insert(joinPath(testPath, "1")); + all_folders.insert(joinPath(testPath, "2")); + all_folders.insert(joinPath(testPath, "zero0")); + all_folders.insert(joinPath(testPath, joinPath("zero0", "test"))); + + not_exist_folders.clear(); + not_exist_folders.insert(joinPath(testPath, "999")); + not_exist_folders.insert(joinPath(testPath, joinPath("zxcb", "999"))); + not_exist_folders.insert(joinPath(testPath, "8")); + + existance_files.clear(); + existance_files.insert(joinPath(testPath, "emptyFile.js")); + existance_files.insert(joinPath(testPath, joinPath("2", "22222.js"))); + existance_files.insert(joinPath(testPath, "notempty.txt")); + existance_files.insert(joinPath(testPath, "notemptyx2")); + + not_existance_files.clear(); + not_existance_files.insert(joinPath(testPath, "bemptyFile.js")); + not_existance_files.insert(joinPath(testPath, joinPath("2", "b22222.js"))); + not_existance_files.insert(joinPath(testPath, "bnotempty.txt")); + not_existance_files.insert(joinPath(testPath, "bnotemptyx2")); + + empty_files.clear(); + empty_files.insert(joinPath(testPath, "emptyFile.js")); + empty_files.insert(joinPath(testPath, joinPath("2", "22222.js"))); + + not_empty_files.clear(); + not_empty_files.push_back({joinPath(testPath, "notempty.txt"), testString.size()}); + not_empty_files.push_back({joinPath(testPath, "notemptyx2"), 2*testString.size()}); + } + + bool initializeTree(const Options& options) { + initTestPaths(options); - if (p) + if (!Cpl::CreatePath(joinPath(testPath, "1")) || + !Cpl::CreatePath(joinPath(testPath, "2")) || + !Cpl::CreatePath(joinPath(testPath, joinPath("zero0", "test")))) return false; std::ofstream f1(joinPath(testPath, "emptyFile.js")); @@ -827,7 +806,7 @@ namespace Test CPL_LOG_SS(Info, "Filesystem " << Cpl::FilesystemType()); CPL_LOG_SS(Info, "Compiler type " << Cpl::CompilerType()); - initializeTree(); + initializeTree(options); ok &= COMPARE_RESULT(Modify::folders(), 1); ok &= COMPARE_RESULT(Modify::createFiles(), 1); ok &= COMPARE_RESULT(Modify::readFormatsTest(), 1); @@ -846,7 +825,7 @@ namespace Test CPL_LOG_SS(Info, "Filesystem " << Cpl::FilesystemType()); CPL_LOG_SS(Info, "Compiler type " << Cpl::CompilerType()); - initializeTree(); + initializeTree(options); ok &= COMPARE_RESULT(Existance::testFileExists(), 1); ok &= COMPARE_RESULT(Existance::testFolderExists(), 1); @@ -864,7 +843,7 @@ namespace Test CPL_LOG_SS(Info, "Filesystem " << Cpl::FilesystemType()); CPL_LOG_SS(Info, "Compiler type " << Cpl::CompilerType()); - initializeTree(); + initializeTree(options); ok &= COMPARE_RESULT(Info::fileList(), 1); ok &= COMPARE_RESULT(Info::naming(), 1); ok &= COMPARE_RESULT(Info::extension(), 1); diff --git a/src/Test/TestLog.cpp b/src/Test/TestLog.cpp index 11b5f7f..0a207d4 100644 --- a/src/Test/TestLog.cpp +++ b/src/Test/TestLog.cpp @@ -38,7 +38,7 @@ namespace Test bool LogCallbackTest(const Options& options) { - std::ofstream ofs("custom_log.txt"); + std::ofstream ofs(options.OutputPath("custom_log.txt")); int id = Cpl::Log::Global().AddWriter(Log::Debug, CustomFileWriter, &ofs); CPL_LOG(Debug, "debug log message"); @@ -59,7 +59,7 @@ namespace Test bool LogCallbackRawTest(const Options& options) { - std::ofstream ofs("custom_raw_log.txt"); + std::ofstream ofs(options.OutputPath("custom_raw_log.txt")); int id = Cpl::Log::Global().AddWriter(Log::Debug, CustomRawFileWriter, &ofs); CPL_LOG(Debug, "raw debug log message"); @@ -98,10 +98,10 @@ namespace Test bool LogIdTest(const Options& options) { - std::ofstream ofs1("log_1.txt"); + std::ofstream ofs1(options.OutputPath("log_1.txt")); int id1 = Cpl::Log::Global().AddWriter(Log::Debug, CustomFileWriter, &ofs1); - std::ofstream ofs2("log_2.txt"); + std::ofstream ofs2(options.OutputPath("log_2.txt")); int id2 = Cpl::Log::Global().AddWriter(Log::Debug, CustomFileWriter, &ofs2); CPL_LOG_ID(Debug, "log 1 message", id1); diff --git a/src/Test/TestOptions.h b/src/Test/TestOptions.h index 462e578..f284dfb 100644 --- a/src/Test/TestOptions.h +++ b/src/Test/TestOptions.h @@ -36,6 +36,7 @@ namespace Test bool help; Log::Level logLevel; String logFile; + String output; Strings include, exclude; Options(int argc, char* argv[]) @@ -44,10 +45,16 @@ namespace Test help = HasArg("-h", "-?"); logLevel = (Log::Level)Cpl::ToVal(GetArg2("-ll", "--logLevel", "4", false)); logFile = GetArg2("-lf", "--logFile", "", false); + output = GetArg("-o", "out", false); include = GetArgs("-i", Strings(), false); exclude = GetArgs("-e", Strings(), false); } bool Required(const Group& group); + + String OutputPath(const String& name) const + { + return Cpl::MakePath(output, name); + } }; } diff --git a/src/Test/TestParam.cpp b/src/Test/TestParam.cpp index bf1c739..c1aaa4e 100644 --- a/src/Test/TestParam.cpp +++ b/src/Test/TestParam.cpp @@ -44,10 +44,10 @@ namespace Test test().value() = 9; - test.Save("simple_short.xml", false); - test.Save("simple_full.xml", true); + test.Save(options.OutputPath("simple_short.xml"), false); + test.Save(options.OutputPath("simple_full.xml"), true); - if (!loaded.Load("simple_short.xml")) + if (!loaded.Load(options.OutputPath("simple_short.xml"))) return false; return loaded.Equal(test); @@ -99,10 +99,10 @@ namespace Test test().child().name() = "Horse"; - test.Save("struct_short.xml", false); - test.Save("struct_full.xml", true); + test.Save(options.OutputPath("struct_short.xml"), false); + test.Save(options.OutputPath("struct_full.xml"), true); - if (!loaded.Load("struct_full.xml")) + if (!loaded.Load(options.OutputPath("struct_full.xml"))) return false; return loaded.Equal(test); @@ -144,10 +144,10 @@ namespace Test TestParamHolder test, loaded; - test.Save("struct_mod_short.xml", false); - test.Save("struct_mod_full.xml", true); + test.Save(options.OutputPath("struct_mod_short.xml"), false); + test.Save(options.OutputPath("struct_mod_full.xml"), true); - if (!loaded.Load("struct_mod_full.xml")) + if (!loaded.Load(options.OutputPath("struct_mod_full.xml"))) return false; return loaded.Equal(test); @@ -177,10 +177,10 @@ namespace Test test().children().resize(2); test().children()[0].value() = 5; - test.Save("vector_short.xml", false); - test.Save("vector_full.xml", true); + test.Save(options.OutputPath("vector_short.xml"), false); + test.Save(options.OutputPath("vector_full.xml"), true); - if (!loaded.Load("vector_short.xml")) + if (!loaded.Load(options.OutputPath("vector_short.xml"))) return false; return loaded.Equal(test); @@ -234,10 +234,10 @@ namespace Test test().enum2() = A::B::Enum2; test().enum3() = A::B::C::Enum1; - test.Save("enum_short.xml", false); - test.Save("enum_full.xml", true); + test.Save(options.OutputPath("enum_short.xml"), false); + test.Save(options.OutputPath("enum_full.xml"), true); - if (!loaded.Load("enum_full.xml")) + if (!loaded.Load(options.OutputPath("enum_full.xml"))) return false; return loaded.Equal(test); @@ -269,10 +269,10 @@ namespace Test copy.Clone(test); - test.Save("map_short.xml", false); - copy.Save("map_copy_full.xml", true); + test.Save(options.OutputPath("map_short.xml"), false); + copy.Save(options.OutputPath("map_copy_full.xml"), true); - if (!loaded.Load("map_copy_full.xml")) + if (!loaded.Load(options.OutputPath("map_copy_full.xml"))) return false; return loaded.Equal(test); @@ -340,10 +340,10 @@ namespace Test copy.Clone(test); - test.Save("map_short.xml", false); - copy.Save("map_copy_full.xml", true); + test.Save(options.OutputPath("map_short.xml"), false); + copy.Save(options.OutputPath("map_copy_full.xml"), true); - if (!loaded.Load("map_copy_full.xml")) + if (!loaded.Load(options.OutputPath("map_copy_full.xml"))) return false; return loaded.Equal(test); @@ -370,10 +370,10 @@ namespace Test test().value() = 9; - test.Save("limited_short.xml", false); - test.Save("limited_full.xml", true); + test.Save(options.OutputPath("limited_short.xml"), false); + test.Save(options.OutputPath("limited_full.xml"), true); - if (!loaded.Load("limited_full.xml")) + if (!loaded.Load(options.OutputPath("limited_full.xml"))) return false; return loaded.Equal(test); @@ -409,20 +409,20 @@ namespace Test test().strProp().value() = "string"; - test.Save("template_short.yml", false); - test.Save("template_full.yml", true); + test.Save(options.OutputPath("template_short.yml"), false); + test.Save(options.OutputPath("template_full.yml"), true); - test.Save("template_short.xml", false); - test.Save("template_full.xml", true); + test.Save(options.OutputPath("template_short.xml"), false); + test.Save(options.OutputPath("template_full.xml"), true); - if (!loaded.Load("template_full.yml")) + if (!loaded.Load(options.OutputPath("template_full.yml"))) return false; if (!loaded.Equal(test)) { CPL_LOG_SS(Error, "loaded full != original"); - loaded.Save("template_short_loaded.yml", false); - loaded.Save("template_full_loaded.yml", true); + loaded.Save(options.OutputPath("template_short_loaded.yml"), false); + loaded.Save(options.OutputPath("template_full_loaded.yml"), true); return false; } diff --git a/src/Test/TestParamV2.cpp b/src/Test/TestParamV2.cpp index 1f17693..a8ff91e 100644 --- a/src/Test/TestParamV2.cpp +++ b/src/Test/TestParamV2.cpp @@ -51,10 +51,10 @@ namespace Test test().children().resize(2); test().children()[0].value() = 5; - test.Save("vector_v2_short.xml", false); - test.Save("vector_v2_full.xml", true); + test.Save(options.OutputPath("vector_v2_short.xml"), false); + test.Save(options.OutputPath("vector_v2_full.xml"), true); - if (!loaded.Load("vector_v2_short.xml")) + if (!loaded.Load(options.OutputPath("vector_v2_short.xml"))) return false; return loaded.Equal(test); @@ -86,10 +86,10 @@ namespace Test copy.Clone(test); - test.Save("map_v2_short.xml", false); - copy.Save("map_v2_copy_full.xml", true); + test.Save(options.OutputPath("map_v2_short.xml"), false); + copy.Save(options.OutputPath("map_v2_copy_full.xml"), true); - if (!loaded.Load("map_v2_copy_full.xml")) + if (!loaded.Load(options.OutputPath("map_v2_copy_full.xml"))) return false; return loaded.Equal(test); diff --git a/src/Test/TestProp.cpp b/src/Test/TestProp.cpp index 9850057..40da537 100644 --- a/src/Test/TestProp.cpp +++ b/src/Test/TestProp.cpp @@ -61,17 +61,17 @@ namespace Test test.SetProperty("first.name", "new_name"); - test.Save("prop_short.xml", false); - test.Save("prop_full.xml", true); + test.Save(options.OutputPath("prop_short.xml"), false); + test.Save(options.OutputPath("prop_full.xml"), true); - if (!loaded.Load("prop_full.xml")) + if (!loaded.Load(options.OutputPath("prop_full.xml"))) return false; if (!loaded.Equal(test)) { CPL_LOG_SS(Error, "loaded full != original"); - loaded.Save("prop_short_loaded.xml", false); - loaded.Save("prop_gfull_loaded.xml", true); + loaded.Save(options.OutputPath("prop_short_loaded.xml"), false); + loaded.Save(options.OutputPath("prop_gfull_loaded.xml"), true); return false; } diff --git a/src/Test/TestTable.cpp b/src/Test/TestTable.cpp index 5308d38..1045bb6 100644 --- a/src/Test/TestTable.cpp +++ b/src/Test/TestTable.cpp @@ -58,7 +58,7 @@ namespace Test CPL_LOG_SS(Info, std::endl << table.GenerateText()); - std::ofstream ofsHtml("simple_table.html"); + std::ofstream ofsHtml(options.OutputPath("simple_table.html")); if (ofsHtml.is_open()) { ofsHtml << "" << std::endl; @@ -68,7 +68,7 @@ namespace Test ofsHtml.close(); } - std::ofstream ofsText("simple_table.txt"); + std::ofstream ofsText(options.OutputPath("simple_table.txt")); if (ofsText.is_open()) { ofsText << "simple table" << std::endl << std::endl; @@ -85,7 +85,7 @@ namespace Test CPL_LOG_SS(Info, std::endl << table.GenerateText()); - std::ofstream ofs("sortable_table.html"); + std::ofstream ofs(options.OutputPath("sortable_table.html")); if (ofs.is_open()) { ofs << "" << std::endl; diff --git a/src/Test/TestYaml.cpp b/src/Test/TestYaml.cpp index 3a48eaa..f20b6b6 100644 --- a/src/Test/TestYaml.cpp +++ b/src/Test/TestYaml.cpp @@ -98,27 +98,27 @@ namespace Test test().dict()["A"].desc() = "A"; test().dict()["B"]; - test.Save("yaml_short.yml", false, Cpl::ParamFormatYaml); + test.Save(options.OutputPath("yaml_short.yml"), false, Cpl::ParamFormatYaml); - test.Save("yaml_full.yml", true, Cpl::ParamFormatYaml); + test.Save(options.OutputPath("yaml_full.yml"), true, Cpl::ParamFormatYaml); - if (!loaded.Load("yaml_short.yml", Cpl::ParamFormatYaml)) + if (!loaded.Load(options.OutputPath("yaml_short.yml"), Cpl::ParamFormatYaml)) return false; if (!loaded.Equal(test)) { CPL_LOG_SS(Error, "loaded short != original"); - loaded.Save("yaml_short_loaded.yml", false, Cpl::ParamFormatYaml); - loaded.Save("yaml_full_loaded.yml", true, Cpl::ParamFormatYaml); + loaded.Save(options.OutputPath("yaml_short_loaded.yml"), false, Cpl::ParamFormatYaml); + loaded.Save(options.OutputPath("yaml_full_loaded.yml"), true, Cpl::ParamFormatYaml); return false; } - if (!loaded.Load("yaml_full.yml", Cpl::ParamFormatYaml)) + if (!loaded.Load(options.OutputPath("yaml_full.yml"), Cpl::ParamFormatYaml)) return false; if (!loaded.Equal(test)) { CPL_LOG_SS(Error, "loaded full != original"); - loaded.Save("yaml_short_loaded.yml", false, Cpl::ParamFormatYaml); - loaded.Save("yaml_full_loaded.yml", true, Cpl::ParamFormatYaml); + loaded.Save(options.OutputPath("yaml_short_loaded.yml"), false, Cpl::ParamFormatYaml); + loaded.Save(options.OutputPath("yaml_full_loaded.yml"), true, Cpl::ParamFormatYaml); return false; }