1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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 for path " << path << ", 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
|