1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2000-2014 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 "tcov2lcov.hh"
16 #include <sys/types.h>
20 #include <libxml/xmlschemastypes.h>
21 #include <libxml/xmlreader.h>
23 #include "../common/version_internal.h"
24 #include "../common/license.h"
26 // The new (T)itan(C)overage(D)ata is born!
27 const std::string
tcd_ext(".tcd");
28 size_t tcd_ext_len
= tcd_ext
.size();
36 void TcovData::inc_function(const std::string
& function
, int n
)
38 if (m_functions
.count(function
) == 0) m_functions
[function
] = 0;
39 m_functions
[function
] += n
;
42 void TcovData::inc_line(int line
, int n
)
44 if (m_lines
.count(line
) == 0) m_lines
[line
] = 0;
48 Tcov2Lcov::Tcov2Lcov(const char *code_base
, const char *input_dir
, const char *output_file
, const char *xsd_file
)
49 : m_code_base(code_base
), m_input_dir(input_dir
), m_output_file(output_file
), m_xsd_file(xsd_file
)
51 m_ver_major
= m_ver_minor
= -1;
54 Tcov2Lcov::~Tcov2Lcov()
56 std::map
<std::string
, TcovData
*>::iterator i
= m_data
.begin();
57 for (; i
!= m_data
.end(); ++i
) {
63 int Tcov2Lcov::collect_dir(std::string dir
)
66 if ((d
= opendir(dir
.c_str())) == NULL
) {
67 // Fatal for top-level directory.
68 std::cerr
<< "Error while opening directory `" << dir
<< "'" << std::endl
;
71 struct dirent
*de
= NULL
;
72 while ((de
= readdir(d
)) != NULL
) {
73 std::string
file_name(de
->d_name
);
74 std::string full_file_name
= dir
+ P_SEP
+ file_name
;
75 // No `d_type' field on Solaris...
77 int stat_ret_val
= stat(de
->d_name
, &st
);
78 if (stat_ret_val
!= 0)
80 if (S_ISDIR(st
.st_mode
)) {
81 if (file_name
== ".." || file_name
== ".")
83 collect_dir(full_file_name
);
85 size_t file_name_len
= file_name
.size();
86 if (file_name_len
> tcd_ext_len
87 && file_name
.substr(file_name_len
- tcd_ext_len
) == tcd_ext
) {
88 m_files
.push_back(full_file_name
);
96 int Tcov2Lcov::collect()
98 if (collect_dir(m_input_dir
) < 0)
100 return static_cast<int>(m_files
.size());
103 void Tcov2Lcov::d_print_files() const
105 std::cout
<< "Collected XML/TCD files:" << std::endl
;
106 for (size_t i
= 0; i
< m_files
.size(); ++i
)
107 std::cout
<< "#" << i
<< ": " << m_files
[i
] << std::endl
;
110 int Tcov2Lcov::validate() const
113 xmlLineNumbersDefault(1);
115 xmlSchemaParserCtxtPtr ctxt
= xmlSchemaNewParserCtxt(m_xsd_file
.c_str());
116 xmlSchemaSetParserErrors(ctxt
, (xmlSchemaValidityErrorFunc
)fprintf
,
117 (xmlSchemaValidityWarningFunc
)fprintf
, stderr
);
118 // valgrind reports `Conditional jump or move depends on uninitialised
119 // value(s)' even for xmllint when using xmlSchemaParse().
120 xmlSchemaPtr schema
= xmlSchemaParse(ctxt
);
121 xmlSchemaFreeParserCtxt(ctxt
);
122 if (schema
== NULL
) {
123 xmlSchemaCleanupTypes();
129 for (size_t i
= 0; i
< m_files
.size(); ++i
) {
130 const char *xml_file_name
= m_files
[i
].c_str();
131 xmlDocPtr doc
= xmlReadFile(xml_file_name
, NULL
, 0);
133 std::cerr
<< "Could not parse `" << xml_file_name
<< "'" << std::endl
;
137 xmlSchemaValidCtxtPtr xml_ctxt
= xmlSchemaNewValidCtxt(schema
);
138 xmlSchemaSetValidErrors(xml_ctxt
, (xmlSchemaValidityErrorFunc
)fprintf
,
139 (xmlSchemaValidityWarningFunc
)fprintf
, stderr
);
140 int valid
= xmlSchemaValidateDoc(xml_ctxt
, doc
);
142 std::cerr
<< "`" << xml_file_name
<< "' fails to validate" << std::endl
;
145 xmlSchemaFreeValidCtxt(xml_ctxt
);
149 xmlSchemaFree(schema
);
150 xmlSchemaCleanupTypes();
156 const char *get_next_attr(const xmlTextReaderPtr
& reader
)
158 xmlTextReaderMoveToNextAttribute(reader
);
159 return (const char *)xmlTextReaderConstValue(reader
);
162 int Tcov2Lcov::merge()
164 for (size_t i
= 0; i
< m_files
.size(); ++i
) {
165 std::string
& file_name
= m_files
[i
];
166 xmlInitParser(); // Avoid weird memory leaks...
167 xmlTextReaderPtr reader
= xmlReaderForFile(file_name
.c_str(), NULL
, 0);
168 if (reader
== NULL
) {
169 std::cerr
<< "Unable to open `" << file_name
<< "'" << std::endl
;
172 std::string current_file_name
;
173 while (xmlTextReaderRead(reader
)) {
174 switch (xmlTextReaderNodeType(reader
)) {
175 case XML_READER_TYPE_ELEMENT
: {
176 std::string
elem((const char *)xmlTextReaderConstName(reader
));
177 if (elem
== "version") {
178 int major
= atoi(get_next_attr(reader
));
179 int minor
= atoi(get_next_attr(reader
));
180 if (m_ver_major
== -1) m_ver_major
= major
;
181 if (m_ver_minor
== -1) m_ver_minor
= minor
;
182 if (m_ver_major
!= major
|| m_ver_minor
!= minor
) {
183 std::cerr
<< "Version mismatch in `" << file_name
<< "' ("
184 << major
<< "." << minor
<< " vs. " << m_ver_major
185 << "." << m_ver_minor
<< ")" << std::endl
;
186 xmlFreeTextReader(reader
);
191 } else if (elem
== "file") {
192 current_file_name
= get_next_attr(reader
);
193 if (m_data
.count(current_file_name
) == 0)
194 m_data
[current_file_name
] = new TcovData();
195 } else if (elem
== "function") {
196 std::string
function_name(get_next_attr(reader
));
197 int function_count
= atoi(get_next_attr(reader
));
198 m_data
[current_file_name
]->inc_function(function_name
, function_count
);
199 } else if (elem
== "line") {
200 int line_no
= atoi(get_next_attr(reader
));
201 int line_count
= atoi(get_next_attr(reader
));
202 m_data
[current_file_name
]->inc_line(line_no
, line_count
);
204 xmlTextReaderMoveToElement(reader
);
212 xmlFreeTextReader(reader
);
219 int Tcov2Lcov::generate()
222 if (!(stat(m_code_base
.c_str(), &st
) == 0 && S_ISDIR(st
.st_mode
))) {
223 std::cerr
<< "Invalid code base `" << m_code_base
<< "'" << std::endl
;
226 std::ofstream
output(m_output_file
.c_str());
227 if (!output
.is_open()) {
228 std::cerr
<< "Output file `" << m_output_file
<< "' cannot be opened" << std::endl
;
231 output
<< "TN:" << std::endl
;
232 std::map
<std::string
, TcovData
*>::const_iterator i
= m_data
.begin();
233 for (; i
!= m_data
.end(); ++i
) {
234 output
<< "SF:" << m_code_base
<< P_SEP
<< (*i
).first
<< std::endl
;
235 const TcovData
*data
= (*i
).second
;
236 const std::map
<std::string
, int>& functions
= data
->get_functions();
237 const std::map
<int, int>& lines
= data
->get_lines();
238 std::map
<std::string
, int>::const_iterator j
= functions
.begin();
239 std::map
<int, int>::const_iterator k
= lines
.begin();
240 for (; j
!= functions
.end(); ++j
) // All functions.
241 output
<< "FN:0," << (*j
).first
<< std::endl
;
242 for (j
= functions
.begin(); j
!= functions
.end(); ++j
) // All functions counted.
243 output
<< "FNDA:" << (*j
).second
<< "," << (*j
).first
<< std::endl
;
244 for (; k
!= lines
.end(); ++k
) // All lines counted.
245 output
<< "DA:" << (*k
).first
<< "," << (*k
).second
<< std::endl
;
246 output
<< "end_of_record" << std::endl
;
252 static void print_product_info()
254 std::cerr
<< "TCD to LCOV Converter for the TTCN-3 Test Executor, version "
255 << PRODUCT_NUMBER
<< std::endl
;
258 static void print_version()
260 std::cerr
<< "Product number: " << PRODUCT_NUMBER
<< std::endl
261 << "Build date: " << __DATE__
<< " " << __TIME__
<< std::endl
262 << "Compiled with: " << C_COMPILER_VERSION
<< std::endl
<< std::endl
263 << COPYRIGHT_STRING
<< std::endl
<< std::endl
;
266 static void print_help(const char *name
)
268 std::cerr
<< "Usage: " << name
<< " [-hv] [-b dir] [-d dir] [-o file] [-x xsd] " << std::endl
269 << " -b dir\tSet code base for source files" << std::endl
270 << " -d dir\tCollect XML/TCD files from `dir'" << std::endl
271 << " -h\t\tPrint help" << std::endl
272 << " -o file\tPlace of the output file" << std::endl
273 << " -v\t\tShow version" << std::endl
274 << " -x xsd\tValidate all XMLs/TCDs against this `xsd'" << std::endl
;
277 int main(int argc
, char *argv
[])
279 // Unfortunately, getopt_long() and :: for optional parameters are GNU extensions and not present on Solaris...
280 // -b --dir: Set code base for source files (_optional_)
281 // -d --dir: Collect XML/TCD files from, default is `.' the current directory (_optional_)
282 // -o --output: Place of the output, default is `./output.info' (_optional_)
283 // -x --xsd: Validate all XMLs/TCDs against this XSD (_optional_)
284 // -h --help: Display help (_optional_)
285 const char *code_base
= NULL
, *input_dir
= NULL
, *output_file
= NULL
, *xsd_file
= NULL
;
287 while ((c
= getopt(argc
, argv
, "b:d:ho:vx:")) != -1) {
296 print_product_info();
300 output_file
= optarg
;
303 print_product_info();
306 print_license_info();
317 bool param_error
= false;
319 #define PATH_MAX 1024
322 memset(cwd
, 0, PATH_MAX
);
324 if (!getcwd(cwd
, PATH_MAX
)) {
325 std::cerr
<< "Unable to get current working directory for `-b'" << std::endl
;
327 } else code_base
= cwd
;
329 // Leave it relative.
330 if (!input_dir
) input_dir
= ".";
331 if (!output_file
) output_file
= "output.info";
332 std::string xsd_file_str
;
334 const char *ttcn3_dir
= getenv("TTCN3_DIR");
336 std::cerr
<< "$TTCN3_DIR environment variable is not set" << std::endl
;
339 xsd_file_str
= std::string(ttcn3_dir
) + P_SEP
+ std::string("include") + P_SEP
+ std::string("tcov.xsd");
340 xsd_file
= xsd_file_str
.c_str();
352 load_license (&lstr
);
353 int license_valid
= verify_license(&lstr
);
354 free_license (&lstr
);
356 if (!license_valid
) {
362 Tcov2Lcov
t2l(code_base
, input_dir
, output_file
, xsd_file
);
363 int num_files
= t2l
.collect();
365 std::cerr
<< "Directory `" << input_dir
<< "' doesn't exist" << std::endl
;
367 } else if (num_files
== 0) {
368 std::cerr
<< "No data files were found in the given directory `"
369 << input_dir
<< "'" << std::endl
;
372 // Initialize the library and check potential ABI mismatches between
373 // the between the version it was compiled for and the actual shared
376 if (t2l
.validate() < 0) {
377 std::cerr
<< "Error while validating XML/TCD files" << std::endl
;
380 if (t2l
.merge() < 0) {
381 std::cerr
<< "Error while merging XML/TCD files" << std::endl
;
384 if (t2l
.generate() < 0) {
385 std::cerr
<< "Error while generating output file `" << output_file
<< "'"
392 reffer::reffer(const char*) {}