1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2000-2015 Ericsson Telecom AB
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 "JUnitLogger.hh"
10 #ifndef TITAN_RUNTIME_2
11 #include "RT1/TitanLoggerApi.hh"
13 #include "RT2/TitanLoggerApi.hh"
17 #include <sys/types.h>
23 // It's a static plug-in, destruction is done in the destructor. We need
24 // `extern "C"' for some reason.
25 ILoggerPlugin
*create_junit_logger() { return new JUnitLogger(); }
29 ILoggerPlugin
*create_plugin() { return new JUnitLogger(); }
30 void destroy_plugin(ILoggerPlugin
*plugin
) { delete plugin
; }
33 JUnitLogger::JUnitLogger()
34 : filename_stem_(NULL
), testsuite_name_(mcopystr("Titan"))
35 , filename_(NULL
), file_stream_(NULL
)
37 // Overwrite values set by the base class constructor
40 name_
= mcopystr("JUnitLogger");
41 help_
= mcopystr("JUnitLogger writes JUnit-compatible XML");
42 //printf("%5lu:constructed\n", (unsigned long)getpid());
45 JUnitLogger::~JUnitLogger()
47 //printf("%5lu:desstructed\n", (unsigned long)getpid());
53 Free(testsuite_name_
);
55 name_
= help_
= filename_
= filename_stem_
= NULL
;
59 void JUnitLogger::init(const char */
*options*/
)
61 //printf("%5lu:init\n", (unsigned long)getpid());
62 fprintf(stderr
, "Initializing `%s' (v%u.%u): %s\n", name_
,
63 major_version_
, minor_version_
, help_
);
66 void JUnitLogger::fini()
69 //fprintf(stderr, "JUnitLogger finished logging for PID: `%lu'\n", (unsigned long)getpid());
72 void JUnitLogger::set_parameter(
73 const char *parameter_name
, const char *parameter_value
)
76 if (!strcmp("filename_stem", parameter_name
)) {
77 if (filename_stem_
!= NULL
)
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
);
85 fprintf(stderr
, "Unsupported parameter: `%s' with value: `%s'\n",
86 parameter_name
, parameter_value
);
90 void JUnitLogger::open_file(bool is_first
)
93 if (filename_stem_
== NULL
) {
94 filename_stem_
= mcopystr("junit-xml");
98 if (file_stream_
!= NULL
) return; // already open
100 if (!TTCN_Runtime::is_single()
101 && !TTCN_Runtime::is_mtc()) return; // don't bother, only MTC has testcases
103 filename_
= mprintf("%s-%lu.log", filename_stem_
, (unsigned long)getpid());
104 //printf("\n%5lu:open [%s]\n", (unsigned long)getpid(), filename_);
106 file_stream_
= fopen(filename_
, "w");
108 fprintf(stderr
, "%s was unable to open log file `%s', reinitialization "
109 "may help\n", plugin_name(), filename_
);
113 is_configured_
= true;
115 fprintf(file_stream_
,
116 "<?xml version=\"1.0\"?>\n"
117 "<testsuite name='%s'>"
118 // We don't know the number of tests yet; leave out the tests=... attrib
119 "<!-- logger name=\"%s\" version=\"v%u.%u\" -->\n",
121 plugin_name(), major_version(), minor_version());
122 fflush(file_stream_
);
125 void JUnitLogger::close_file()
127 //printf("%5lu:close%c[%s]\n", (unsigned long)getpid(), file_stream_ ? ' ' : 'd', filename_ ? filename_ : "<none>");
128 if (file_stream_
!= NULL
) {
129 fputs("</testsuite>\n", file_stream_
);
130 fflush(file_stream_
);
131 fclose(file_stream_
);
140 void JUnitLogger::log(const TitanLoggerApi::TitanLogEvent
& event
,
141 bool /*log_buffered*/, bool /*separate_file*/,
142 bool /*use_emergency_mask*/)
145 if (file_stream_
== NULL
) return;
146 static RInt seconds
, microseconds
;
148 const TitanLoggerApi::LogEventType_choice
& choice
= event
.logEvent().choice();
150 switch (choice
.get_selection()) {
151 case TitanLoggerApi::LogEventType_choice::ALT_testcaseOp
: {
152 const TitanLoggerApi::TestcaseEvent_choice
& tcev
= choice
.testcaseOp().choice();
153 switch (tcev
.get_selection()) {
154 case TitanLoggerApi::TestcaseEvent_choice::ALT_testcaseStarted
: {
155 fprintf(file_stream_
, "<!-- Testcase %s started -->\n",
156 (const char*)tcev
.testcaseStarted().testcase__name());
157 const TitanLoggerApi::TimestampType
& ts
= event
.timestamp();
158 // remember the start time
159 seconds
= ts
.seconds();
160 microseconds
= ts
.microSeconds();
163 case TitanLoggerApi::TestcaseEvent_choice::ALT_testcaseFinished
: {
164 const TitanLoggerApi::TestcaseType
& tct
= tcev
.testcaseFinished();
165 const TitanLoggerApi::TimestampType
& ts
= event
.timestamp();
166 long long now
= 1000000LL * (long long)ts
.seconds() + (long long)ts
.microSeconds();
167 long long then
= 1000000LL * (long long) seconds
+ (long long) microseconds
;
168 fprintf(file_stream_
, "<!-- Testcase %s finished in %f, verdict: %s%s%s -->\n",
169 (const char*)tct
.name().testcase__name(),
170 (now
- then
) / 1000000.0,
171 verdict_name
[tct
.verdict()],
172 (tct
.reason().lengthof() > 0 ? ", reason: " : ""),
173 (const char*)tct
.reason());
175 fprintf(file_stream_
, " <testcase classname='%s' name='%s' time='%f'>\n",
176 (const char*)tct
.name().module__name(),
177 (const char*)tct
.name().testcase__name(),
178 (now
- then
) / 1000000.0);
180 switch (tct
.verdict()) {
181 case TitanLoggerApi::Verdict::UNBOUND_VALUE
:
182 case TitanLoggerApi::Verdict::UNKNOWN_VALUE
:
186 case TitanLoggerApi::Verdict::v0none
:
187 fprintf(file_stream_
, " <skipped>no verdict</skipped>\n");
190 case TitanLoggerApi::Verdict::v1pass
:
191 // do nothing; this counts as success
194 case TitanLoggerApi::Verdict::v2inconc
:
195 // JUnit doesn't seem to have the concept of "inconclusive"
196 // do nothing; this will appear as success
199 case TitanLoggerApi::Verdict::v3fail
: {
200 fprintf(file_stream_
, " <failure type='fail-verdict'>%s\n",
201 (const char*)escape_xml_element(tct
.reason()));
204 const TitanLoggerApi::TitanLogEvent_sourceInfo__list
& stack
=
205 event
.sourceInfo__list();
206 int stack_depth
= stack
.size_of();
207 for (int i
=0; i
< stack_depth
; ++i
) {
208 const TitanLoggerApi::LocationInfo
& location
= stack
[i
];
209 fprintf(file_stream_
, "\n %s:%d %s ",
210 (const char*)location
.filename(), (int)location
.line(),
211 (const char*)location
.ent__name());
213 // print the location type
214 switch (location
.ent__type()) {
215 case TitanLoggerApi::LocationInfo_ent__type:: UNKNOWN_VALUE
:
216 case TitanLoggerApi::LocationInfo_ent__type:: UNBOUND_VALUE
:
219 case TitanLoggerApi::LocationInfo_ent__type::unknown
:
222 case TitanLoggerApi::LocationInfo_ent__type::controlpart
:
223 fputs("control part", file_stream_
);
225 case TitanLoggerApi::LocationInfo_ent__type::testcase__
:
226 fputs("testcase", file_stream_
);
228 case TitanLoggerApi::LocationInfo_ent__type::altstep__
:
229 fputs("altstep", file_stream_
);
231 case TitanLoggerApi::LocationInfo_ent__type::function__
:
232 fputs("function", file_stream_
);
234 case TitanLoggerApi::LocationInfo_ent__type::external__function
:
235 fputs("external function", file_stream_
);
237 case TitanLoggerApi::LocationInfo_ent__type::template__
:
238 fputs("template", file_stream_
);
242 fputs("\n </failure>\n", file_stream_
);
245 case TitanLoggerApi::Verdict::v4error
:
246 fprintf(file_stream_
, " <error type='DTE'>%s</error>\n",
247 (const char*)escape_xml_element(tct
.reason()));
250 // error or skip based on verdict
251 fputs(" </testcase>\n", file_stream_
);
254 case TitanLoggerApi::TestcaseEvent_choice::UNBOUND_VALUE
:
255 fputs("<!-- Unbound testcaseOp.choice !! -->\n", file_stream_
);
257 } // switch testcaseOp().choice.get_selection()
259 break; } // testcaseOp
261 case TitanLoggerApi::LogEventType_choice::ALT_errorLog
: {
262 // A DTE is about to be thrown
263 const TitanLoggerApi::Categorized
& cat
= choice
.errorLog();
264 fprintf(file_stream_
, " <error type='DTE'>%s</error>\n",
265 (const char*)escape_xml_element(cat
.text()));
270 } // switch event.logEvent().choice().get_selection()
272 fflush(file_stream_
);
275 CHARSTRING
JUnitLogger::escape_xml(const CHARSTRING
& xml_str
, int escape_chars
)
277 expstring_t escaped
= NULL
;
278 int len
= xml_str
.lengthof();
279 for (int i
=0; i
<len
; i
++) {
280 char c
= *(((const char*)xml_str
)+i
);
283 if (escape_chars
<
) escaped
= mputstr(escaped
, "<");
284 else escaped
= mputc(escaped
, c
);
287 if (escape_chars
>
) escaped
= mputstr(escaped
, ">");
288 else escaped
= mputc(escaped
, c
);
291 if (escape_chars
"
) escaped
= mputstr(escaped
, """);
292 else escaped
= mputc(escaped
, c
);
295 if (escape_chars
&APOS
) escaped
= mputstr(escaped
, "'");
296 else escaped
= mputc(escaped
, c
);
299 if (escape_chars
&
) escaped
= mputstr(escaped
, "&");
300 else escaped
= mputc(escaped
, c
);
303 escaped
= mputc(escaped
, c
);
306 CHARSTRING
ret_val(escaped
);