Sync with 5.4.0
[deliverable/titan.core.git] / loggerplugins / JUnitLogger2 / JUnitLogger2.cc
CommitLineData
a38c6d4c 1///////////////////////////////////////////////////////////////////////////////
3abe9331 2// Copyright (c) 2000-2015 Ericsson Telecom AB
a38c6d4c 3// All rights reserved. This program and the accompanying materials
4// are made available under the terms of the Eclipse Public License v1.0
5// which accompanies this distribution, and is available at
6// http://www.eclipse.org/legal/epl-v10.html
7///////////////////////////////////////////////////////////////////////////////
8#include "JUnitLogger2.hh"
9
10#include <unistd.h>
11#include <sys/types.h>
12
13#include <sys/time.h>
14
15extern "C"
16{
17 // It's a static plug-in, destruction is done in the destructor. We need
18 // `extern "C"' for some reason.
19 ILoggerPlugin *create_junit_logger() { return new JUnitLogger2(); }
20}
21
22extern "C" {
23 ILoggerPlugin *create_plugin() { return new JUnitLogger2(); }
24 void destroy_plugin(ILoggerPlugin *plugin) { delete plugin; }
25}
26
27TestSuite::~TestSuite()
28{
29 for (TestCases::const_iterator it = testcases.begin(); it != testcases.end(); ++it) {
30 delete (*it);
31 }
32}
33
34JUnitLogger2::JUnitLogger2()
35: filename_stem_(NULL), testsuite_name_(mcopystr("Titan")), filename_(NULL), file_stream_(NULL)
36{
37 // Overwrite values set by the base class constructor
38
39 fprintf(stderr, "construct junitlogger\n");
40 major_version_ = 2;
41 minor_version_ = 0;
42 name_ = mcopystr("JUnitLogger");
43 help_ = mcopystr("JUnitLogger writes JUnit-compatible XML");
44 dte_reason = "";
45 //printf("%5lu:constructed\n", (unsigned long)getpid());
46}
47
48JUnitLogger2::~JUnitLogger2()
49{
50 //printf("%5lu:desstructed\n", (unsigned long)getpid());
51 close_file();
52
53 Free(name_);
54 Free(help_);
55 Free(filename_);
56 Free(testsuite_name_);
57 Free(filename_stem_);
58 name_ = help_ = filename_ = filename_stem_ = NULL;
59 file_stream_ = NULL;
60}
61
62void JUnitLogger2::init(const char */*options*/)
63{
64//printf("%5lu:init\n", (unsigned long)getpid());
65 fprintf(stderr, "Initializing `%s' (v%u.%u): %s\n", name_, major_version_, minor_version_, help_);
66}
67
68void JUnitLogger2::fini()
69{
70 //puts("fini");
71 //fprintf(stderr, "JUnitLogger2 finished logging for PID: `%lu'\n", (unsigned long)getpid());
72}
73
74void JUnitLogger2::set_parameter(const char *parameter_name, const char *parameter_value) {
75//puts("set_par");
76 if (!strcmp("filename_stem", parameter_name)) {
77 if (filename_stem_ != NULL)
78 Free(filename_stem_);
79 filename_stem_ = mcopystr(parameter_value);
80 } else if (!strcmp("testsuite_name", parameter_name)) {
81 if (filename_stem_ != NULL)
82 Free(testsuite_name_);
83 testsuite_name_ = mcopystr(parameter_value);
84 } else {
85 fprintf(stderr, "Unsupported parameter: `%s' with value: `%s'\n",
86 parameter_name, parameter_value);
87 }
88}
89
90void JUnitLogger2::open_file(bool is_first) {
91 if (is_first) {
92 if (filename_stem_ == NULL) {
93 filename_stem_ = mcopystr("junit-xml");
94 }
95 }
96
97 if (file_stream_ != NULL) return; // already open
98
99 if (!TTCN_Runtime::is_single() && !TTCN_Runtime::is_mtc()) return; // don't bother, only MTC has testcases
100
101 filename_ = mprintf("%s-%lu.log", filename_stem_, (unsigned long)getpid());
102
103 file_stream_ = fopen(filename_, "w");
104 if (!file_stream_) {
105 fprintf(stderr, "%s was unable to open log file `%s', reinitialization "
106 "may help\n", plugin_name(), filename_);
107 return;
108 }
109
110 is_configured_ = true;
111 time(&(testsuite.start_ts));
112 testsuite.ts_name = testsuite_name_;
113}
114
115void JUnitLogger2::close_file() {
116 if (file_stream_ != NULL) {
117 time(&(testsuite.end_ts));
118 testsuite.write(file_stream_);
119 fclose(file_stream_);
120 file_stream_ = NULL;
121 }
122 if (filename_) {
123 Free(filename_);
124 filename_ = NULL;
125 }
126}
127
128void JUnitLogger2::log(const TitanLoggerApi::TitanLogEvent& event,
129 bool /*log_buffered*/, bool /*separate_file*/,
130 bool /*use_emergency_mask*/)
131{
132 if (file_stream_ == NULL) return;
133
134 const TitanLoggerApi::LogEventType_choice& choice = event.logEvent().choice();
135
136 switch (choice.get_selection()) {
137 case TitanLoggerApi::LogEventType_choice::ALT_testcaseOp: {
138 const TitanLoggerApi::TestcaseEvent_choice& tcev = choice.testcaseOp().choice();
139
140 switch (tcev.get_selection()) {
141 case TitanLoggerApi::TestcaseEvent_choice::ALT_testcaseStarted: {
142 testcase.tc_name = tcev.testcaseStarted().testcase__name();
143 // remember the start time
144 testcase.tc_start = 1000000LL * (long long)event.timestamp().seconds() + (long long)event.timestamp().microSeconds();
145 break; }
146
147 case TitanLoggerApi::TestcaseEvent_choice::ALT_testcaseFinished: {
148 const TitanLoggerApi::TestcaseType& tct = tcev.testcaseFinished();
149 testcase.reason = tct.reason();
150 testcase.module_name = tct.name().module__name();
151
152 const TitanLoggerApi::TimestampType& ts = event.timestamp();
153 long long tc_end = 1000000LL * (long long)ts.seconds() + (long long)ts.microSeconds();
154 testcase.time = (tc_end - testcase.tc_start) / 1000000.0;
155
156 testcase.setTCVerdict(event);
157 testcase.dte_reason = dte_reason.data();
158 dte_reason = "";
159 testsuite.addTestCase(testcase);
160 testcase.reset();
161 break; }
162
163 case TitanLoggerApi::TestcaseEvent_choice::UNBOUND_VALUE:
164 testcase.verdict = TestCase::Unbound;
165 break; } // switch testcaseOp().choice.get_selection()
166
167 break; } // testcaseOp
168
169 case TitanLoggerApi::LogEventType_choice::ALT_errorLog: {// A DTE is about to be thrown
170 const TitanLoggerApi::Categorized& cat = choice.errorLog();
171 dte_reason = escape_xml_element(cat.text());
172 break; }
173
174 default: break;
175 } // switch event.logEvent().choice().get_selection()
176
177 fflush(file_stream_);
178}
179
180CHARSTRING JUnitLogger2::escape_xml(const CHARSTRING& xml_str, int escape_chars) {
181 expstring_t escaped = NULL;
182 int len = xml_str.lengthof();
183 for (int i=0; i<len; i++) {
184 char c = *(((const char*)xml_str)+i);
185 switch (c) {
186 case '<':
187 if (escape_chars&LT) escaped = mputstr(escaped, "&lt;");
188 else escaped = mputc(escaped, c);
189 break;
190 case '>':
191 if (escape_chars&GT) escaped = mputstr(escaped, "&gt;");
192 else escaped = mputc(escaped, c);
193 break;
194 case '"':
195 if (escape_chars&QUOT) escaped = mputstr(escaped, "&quot;");
196 else escaped = mputc(escaped, c);
197 break;
198 case '\'':
199 if (escape_chars&APOS) escaped = mputstr(escaped, "&apos;");
200 else escaped = mputc(escaped, c);
201 break;
202 case '&':
203 if (escape_chars&AMP) escaped = mputstr(escaped, "&amp;");
204 else escaped = mputc(escaped, c);
205 break;
206 default:
207 escaped = mputc(escaped, c);
208 }
209 }
210 CHARSTRING ret_val(escaped);
211 Free(escaped);
212 return ret_val;
213}
214
215void TestCase::writeTestCase(FILE* file_stream_) const{
216 switch(verdict){
217 case None: {
218 fprintf(file_stream_, " <testcase classname='%s' name='%s' time='%f'>\n", module_name.data(), tc_name.data(), time);
219 fprintf(file_stream_, " <skipped>no verdict</skipped>\n");
220 fprintf(file_stream_, " </testcase>\n");
221 break; }
222 case Fail: {
223 fprintf(file_stream_, " <testcase classname='%s' name='%s' time='%f'>\n", module_name.data(), tc_name.data(), time);
224 fprintf(file_stream_, " <failure type='fail-verdict'>%s\n", reason.data());
225 fprintf(file_stream_, "%s\n", stack_trace.data());
226 fprintf(file_stream_, " </failure>\n");
227 fprintf(file_stream_, " </testcase>\n");
228 break; }
229 case Error: {
230 fprintf(file_stream_, " <testcase classname='%s' name='%s' time='%f'>\n", module_name.data(), tc_name.data(), time);
231 fprintf(file_stream_, " <error type='DTE'>%s</error>\n", dte_reason.data());
232 fprintf(file_stream_, " </testcase>\n");
233 break; }
234 default:
235 fprintf(file_stream_, " <testcase classname='%s' name='%s' time='%f'/>\n", module_name.data(), tc_name.data(), time);
236 break;
237 }
238 fflush(file_stream_);
239}
240
241void TestSuite::addTestCase(const TestCase& testcase) {
242 testcases.push_back(new TestCase(testcase));
243 all++;
244 switch(testcase.verdict) {
245 case TestCase::Fail: failed++; break;
246 case TestCase::None: skipped++; break;
247 case TestCase::Error: error++; break;
248 case TestCase::Inconc: inconc++; break;
249 default: break;
250 }
251}
252
253void TestSuite::write(FILE* file_stream_) {
254 double difftime_ = difftime(end_ts, start_ts);
255 fprintf(file_stream_, "<?xml version=\"1.0\"?>\n"
256 "<testsuite name='%s' tests='%d' failures='%d' errors='%d' skipped='%d' inconc='%d' time='%.2f'>\n",
257 ts_name.data(), all, failed, error, skipped, inconc, difftime_);
258 fflush(file_stream_);
259
260 for (TestCases::const_iterator it = testcases.begin(); it != testcases.end(); ++it) {
261 (*it)->writeTestCase(file_stream_);
262 }
263
264 fprintf(file_stream_, "</testsuite>\n");
265 fflush(file_stream_);
266}
267
268void TestCase::setTCVerdict(const TitanLoggerApi::TitanLogEvent& event){
269 TitanLoggerApi::Verdict tc_verdict = event.logEvent().choice().testcaseOp().choice().testcaseFinished().verdict();
270 switch (tc_verdict) {
271 case TitanLoggerApi::Verdict::UNBOUND_VALUE:
272 case TitanLoggerApi::Verdict::UNKNOWN_VALUE:
273 // should not happen
274 break;
275
276 case TitanLoggerApi::Verdict::v0none:
277 verdict = TestCase::None;
278 break;
279
280 case TitanLoggerApi::Verdict::v1pass:
281 verdict = TestCase::Pass;
282 break;
283
284 case TitanLoggerApi::Verdict::v2inconc:
285 verdict = TestCase::Inconc;
286 break;
287
288 case TitanLoggerApi::Verdict::v3fail: {
289 verdict = TestCase::Fail;
290 addStackTrace(event);
291 break; }
292
293 case TitanLoggerApi::Verdict::v4error:
294 verdict = TestCase::Error;
295 break;
296 }
297}
298
299void TestCase::addStackTrace(const TitanLoggerApi::TitanLogEvent& event){
300// Add a stack trace
301 const TitanLoggerApi::TitanLogEvent_sourceInfo__list& stack = event.sourceInfo__list();
302
303 int stack_depth = stack.size_of();
304 for (int i=0; i < stack_depth; ++i) {
305 const TitanLoggerApi::LocationInfo& location = stack[i];
306
307 stack_trace += " ";
308 stack_trace += location.filename();
309 stack_trace += ":";
310 stack_trace += int2str(location.line());
311 stack_trace += " ";
312 stack_trace += location.ent__name();
313 stack_trace += " ";
314
315 // print the location type
316 switch (location.ent__type()) {
317 case TitanLoggerApi::LocationInfo_ent__type:: UNKNOWN_VALUE:
318 case TitanLoggerApi::LocationInfo_ent__type:: UNBOUND_VALUE:
319 // can't happen
320 break;
321 case TitanLoggerApi::LocationInfo_ent__type::unknown:
322 // do nothing
323 break;
324 case TitanLoggerApi::LocationInfo_ent__type::controlpart:
325 stack_trace += "control part";
326 break;
327 case TitanLoggerApi::LocationInfo_ent__type::testcase__:
328 stack_trace += "testcase";
329 break;
330 case TitanLoggerApi::LocationInfo_ent__type::altstep__:
331 stack_trace += "altstep";
332 break;
333 case TitanLoggerApi::LocationInfo_ent__type::function__:
334 stack_trace += "function";
335 break;
336 case TitanLoggerApi::LocationInfo_ent__type::external__function:
337 stack_trace += "external function";
338 break;
339 case TitanLoggerApi::LocationInfo_ent__type::template__:
340 stack_trace += "template";
341 break;
342 }
343
344 if(i<stack_depth-1) stack_trace += "\n";
345 }
346}
This page took 0.064132 seconds and 5 git commands to generate.