diff options
author | alexv-smirnov <alex@ydb.tech> | 2023-03-16 13:59:59 +0300 |
---|---|---|
committer | alexv-smirnov <alex@ydb.tech> | 2023-03-16 13:59:59 +0300 |
commit | 648fc9d7949c88a6ebaaf110289b8b4536bb9a16 (patch) | |
tree | c46c89a2e46516c986af3e4ecb44d927bbdfcbac /library/cpp | |
parent | 4568fe3f6ddb72499ef17f5e0a1e7e2475a9570b (diff) | |
download | ydb-648fc9d7949c88a6ebaaf110289b8b4536bb9a16.tar.gz |
JUnitXML output for unittest
Diffstat (limited to 'library/cpp')
-rw-r--r-- | library/cpp/testing/unittest/CMakeLists.darwin-x86_64.txt | 2 | ||||
-rw-r--r-- | library/cpp/testing/unittest/CMakeLists.linux-aarch64.txt | 2 | ||||
-rw-r--r-- | library/cpp/testing/unittest/CMakeLists.linux-x86_64.txt | 2 | ||||
-rw-r--r-- | library/cpp/testing/unittest/CMakeLists.windows-x86_64.txt | 2 | ||||
-rw-r--r-- | library/cpp/testing/unittest/junit.cpp | 104 | ||||
-rw-r--r-- | library/cpp/testing/unittest/junit.h | 82 | ||||
-rw-r--r-- | library/cpp/testing/unittest/utmain.cpp | 22 | ||||
-rw-r--r-- | library/cpp/testing/unittest/ya.make | 2 |
8 files changed, 218 insertions, 0 deletions
diff --git a/library/cpp/testing/unittest/CMakeLists.darwin-x86_64.txt b/library/cpp/testing/unittest/CMakeLists.darwin-x86_64.txt index 64dc3e1a7a..1aeaa1a60b 100644 --- a/library/cpp/testing/unittest/CMakeLists.darwin-x86_64.txt +++ b/library/cpp/testing/unittest/CMakeLists.darwin-x86_64.txt @@ -11,6 +11,7 @@ add_library(cpp-testing-unittest) target_link_libraries(cpp-testing-unittest PUBLIC contrib-libs-cxxsupp yutil + contrib-libs-libxml library-cpp-colorizer library-cpp-dbg_output library-cpp-diff @@ -21,6 +22,7 @@ target_link_libraries(cpp-testing-unittest PUBLIC target_sources(cpp-testing-unittest PRIVATE ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/gtest.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/checks.cpp + ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/junit.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/plugin.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/registar.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/tests_data.cpp diff --git a/library/cpp/testing/unittest/CMakeLists.linux-aarch64.txt b/library/cpp/testing/unittest/CMakeLists.linux-aarch64.txt index 860a713a0b..bb5187334f 100644 --- a/library/cpp/testing/unittest/CMakeLists.linux-aarch64.txt +++ b/library/cpp/testing/unittest/CMakeLists.linux-aarch64.txt @@ -12,6 +12,7 @@ target_link_libraries(cpp-testing-unittest PUBLIC contrib-libs-linux-headers contrib-libs-cxxsupp yutil + contrib-libs-libxml library-cpp-colorizer library-cpp-dbg_output library-cpp-diff @@ -22,6 +23,7 @@ target_link_libraries(cpp-testing-unittest PUBLIC target_sources(cpp-testing-unittest PRIVATE ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/gtest.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/checks.cpp + ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/junit.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/plugin.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/registar.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/tests_data.cpp diff --git a/library/cpp/testing/unittest/CMakeLists.linux-x86_64.txt b/library/cpp/testing/unittest/CMakeLists.linux-x86_64.txt index 860a713a0b..bb5187334f 100644 --- a/library/cpp/testing/unittest/CMakeLists.linux-x86_64.txt +++ b/library/cpp/testing/unittest/CMakeLists.linux-x86_64.txt @@ -12,6 +12,7 @@ target_link_libraries(cpp-testing-unittest PUBLIC contrib-libs-linux-headers contrib-libs-cxxsupp yutil + contrib-libs-libxml library-cpp-colorizer library-cpp-dbg_output library-cpp-diff @@ -22,6 +23,7 @@ target_link_libraries(cpp-testing-unittest PUBLIC target_sources(cpp-testing-unittest PRIVATE ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/gtest.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/checks.cpp + ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/junit.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/plugin.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/registar.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/tests_data.cpp diff --git a/library/cpp/testing/unittest/CMakeLists.windows-x86_64.txt b/library/cpp/testing/unittest/CMakeLists.windows-x86_64.txt index 64dc3e1a7a..1aeaa1a60b 100644 --- a/library/cpp/testing/unittest/CMakeLists.windows-x86_64.txt +++ b/library/cpp/testing/unittest/CMakeLists.windows-x86_64.txt @@ -11,6 +11,7 @@ add_library(cpp-testing-unittest) target_link_libraries(cpp-testing-unittest PUBLIC contrib-libs-cxxsupp yutil + contrib-libs-libxml library-cpp-colorizer library-cpp-dbg_output library-cpp-diff @@ -21,6 +22,7 @@ target_link_libraries(cpp-testing-unittest PUBLIC target_sources(cpp-testing-unittest PRIVATE ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/gtest.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/checks.cpp + ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/junit.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/plugin.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/registar.cpp ${CMAKE_SOURCE_DIR}/library/cpp/testing/unittest/tests_data.cpp diff --git a/library/cpp/testing/unittest/junit.cpp b/library/cpp/testing/unittest/junit.cpp new file mode 100644 index 0000000000..f5fe84a937 --- /dev/null +++ b/library/cpp/testing/unittest/junit.cpp @@ -0,0 +1,104 @@ +#include "junit.h" + +#include <libxml/xmlwriter.h> +#include <util/system/fs.h> +#include <util/system/file.h> + +namespace NUnitTest { + +#define CHECK_CALL(expr) if ((expr) < 0) { \ + Cerr << "Faield to write to xml" << Endl; \ + return; \ +} + +#define XML_STR(s) ((const xmlChar*)(s)) + +void TJUnitProcessor::Save() { + TString path = FileName; + auto sz = path.size(); + TFile lockFile; + TFile reportFile; + TString lockFileName; + const int MaxReps = 200; +#if defined(_win_) + const char dirSeparator = '\\'; +#else + const char dirSeparator = '/'; +#endif + if ((sz == 0) or (path[sz - 1] == dirSeparator)) { + if (sz > 0) { + NFs::MakeDirectoryRecursive(path); + } + TString reportFileName; + for (int i = 0; i < MaxReps; i++) { + TString suffix = (i > 0) ? ("-" + std::to_string(i)) : ""; + lockFileName = path + ExecName + suffix + ".lock"; + try { + lockFile = TFile(lockFileName, EOpenModeFlag::CreateNew); + } catch (const TFileError&) {} + if (lockFile.IsOpen()) { + // Inside a lock, ensure the .xml file does not exist + reportFileName = path + ExecName + suffix + ".xml"; + try { + reportFile = TFile(reportFileName, EOpenModeFlag::OpenExisting | EOpenModeFlag::RdOnly); + } catch (const TFileError&) { + break; + } + reportFile.Close(); + lockFile.Close(); + NFs::Remove(lockFileName); + } + } + if (!lockFile.IsOpen()) { + Cerr << "Could not find a vacant file name to write report, maximum number of reports: " << MaxReps << Endl; + Y_FAIL("Cannot write report"); + } + path = reportFileName; + } + auto file = xmlNewTextWriterFilename(path.c_str(), 0); + if (!file) { + Cerr << "Failed to open xml file for writing: " << path.c_str() << Endl; + return; + } + + CHECK_CALL(xmlTextWriterStartDocument(file, nullptr, "UTF-8", nullptr)); + CHECK_CALL(xmlTextWriterStartElement(file, XML_STR("testsuites"))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("tests"), XML_STR(ToString(GetTestsCount()).c_str()))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("failures"), XML_STR(ToString(GetFailuresCount()).c_str()))); + + for (const auto& [suiteName, suite] : Suites) { + CHECK_CALL(xmlTextWriterStartElement(file, XML_STR("testsuite"))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("name"), XML_STR(suiteName.c_str()))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("id"), XML_STR(suiteName.c_str()))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("tests"), XML_STR(ToString(suite.GetTestsCount()).c_str()))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("failures"), XML_STR(ToString(suite.GetFailuresCount()).c_str()))); + + for (const auto& [testName, test] : suite.Cases) { + CHECK_CALL(xmlTextWriterStartElement(file, XML_STR("testcase"))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("name"), XML_STR(testName.c_str()))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("id"), XML_STR(testName.c_str()))); + + for (const auto& failure : test.Failures) { + CHECK_CALL(xmlTextWriterStartElement(file, XML_STR("failure"))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("message"), XML_STR(failure.c_str()))); + CHECK_CALL(xmlTextWriterWriteAttribute(file, XML_STR("type"), XML_STR("ERROR"))); + CHECK_CALL(xmlTextWriterEndElement(file)); + } + + CHECK_CALL(xmlTextWriterEndElement(file)); + } + + CHECK_CALL(xmlTextWriterEndElement(file)); + } + + CHECK_CALL(xmlTextWriterEndElement(file)); + CHECK_CALL(xmlTextWriterEndDocument(file)); + xmlFreeTextWriter(file); + + if (lockFile.IsOpen()) { + lockFile.Close(); + NFs::Remove(lockFileName.c_str()); + } +} + +} // namespace NUnitTest diff --git a/library/cpp/testing/unittest/junit.h b/library/cpp/testing/unittest/junit.h new file mode 100644 index 0000000000..f5113c290e --- /dev/null +++ b/library/cpp/testing/unittest/junit.h @@ -0,0 +1,82 @@ +#include "registar.h" + +namespace NUnitTest { + +class TJUnitProcessor : public ITestSuiteProcessor { + struct TTestCase { + TString Name; + bool Success; + TVector<TString> Failures; + + size_t GetFailuresCount() const { + return Failures.size(); + } + }; + + struct TTestSuite { + TMap<TString, TTestCase> Cases; + + size_t GetTestsCount() const { + return Cases.size(); + } + + size_t GetFailuresCount() const { + size_t sum = 0; + for (const auto& [name, testCase] : Cases) { + sum += testCase.GetFailuresCount(); + } + return sum; + } + }; + +public: + TJUnitProcessor(TString file, TString exec) + : FileName(file) + , ExecName(exec) + { + } + + ~TJUnitProcessor() { + Save(); + } + + void OnError(const TError* descr) override { + auto* testCase = GetTestCase(descr->test); + testCase->Failures.emplace_back(descr->msg); + } + + void OnFinish(const TFinish* descr) override { + GetTestCase(descr->test)->Success = descr->Success; + } + +private: + TTestCase* GetTestCase(const TTest* test) { + auto& suite = Suites[test->unit->name]; + return &suite.Cases[test->name]; + } + + void Save(); + + size_t GetTestsCount() const { + size_t sum = 0; + for (const auto& [name, suite] : Suites) { + sum += suite.GetTestsCount(); + } + return sum; + } + + size_t GetFailuresCount() const { + size_t sum = 0; + for (const auto& [name, suite] : Suites) { + sum += suite.GetFailuresCount(); + } + return sum; + } + +private: + TString FileName; + TString ExecName; + TMap<TString, TTestSuite> Suites; +}; + +} // namespace NUnitTest diff --git a/library/cpp/testing/unittest/utmain.cpp b/library/cpp/testing/unittest/utmain.cpp index a55b6f399b..4e10da24e5 100644 --- a/library/cpp/testing/unittest/utmain.cpp +++ b/library/cpp/testing/unittest/utmain.cpp @@ -1,3 +1,4 @@ +#include "junit.h" #include "plugin.h" #include "registar.h" #include "utmain.h" @@ -668,6 +669,16 @@ int NUnitTest::RunMain(int argc, char** argv) { }; EListType listTests = DONT_LIST; + TString oo(getenv("Y_UNITTEST_OUTPUT")); + if (oo.StartsWith("xml:")) { + TStringBuf fileName = oo; + fileName = fileName.SubString(4, TStringBuf::npos); + processor.BeQuiet(); + NUnitTest::ShouldColorizeDiff = false; + processor.SetTraceProcessor(new TJUnitProcessor(TString(fileName), argv[0])); + } + + for (size_t i = 1; i < (size_t)argc; ++i) { const char* name = argv[i]; @@ -723,6 +734,17 @@ int NUnitTest::RunMain(int argc, char** argv) { TString param(argv[i]); size_t assign = param.find('='); Singleton<::NPrivate::TTestEnv>()->AddTestParam(param.substr(0, assign), param.substr(assign + 1)); + } else if (strcmp(name, "--output") == 0) { + ++i; + Y_ENSURE((int)i < argc); + TString param(argv[i]); + if (param.StartsWith("xml:")) { + TStringBuf fileName = param; + fileName = fileName.SubString(4, TStringBuf::npos); + processor.BeQuiet(); + NUnitTest::ShouldColorizeDiff = false; + processor.SetTraceProcessor(new TJUnitProcessor(TString(fileName), argv[0])); + } } else if (TString(name).StartsWith("--")) { return DoUsage(argv[0]), 1; } else if (*name == '-') { diff --git a/library/cpp/testing/unittest/ya.make b/library/cpp/testing/unittest/ya.make index 74bc67e83f..f9fa7a09b3 100644 --- a/library/cpp/testing/unittest/ya.make +++ b/library/cpp/testing/unittest/ya.make @@ -3,6 +3,7 @@ LIBRARY() PROVIDES(test_framework) PEERDIR( + contrib/libs/libxml library/cpp/colorizer library/cpp/dbg_output library/cpp/diff @@ -14,6 +15,7 @@ PEERDIR( SRCS( gtest.cpp checks.cpp + junit.cpp plugin.cpp registar.cpp tests_data.cpp |