1 /******************************************************************************
2 * Copyright (c) 2000-2016 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
14 ******************************************************************************/
15 #include "tcov2lcov.hh"
23 #include <sys/types.h>
27 #include <libxml/xmlschemastypes.h>
28 #include <libxml/xmlreader.h>
30 #include "../common/version_internal.h"
31 #include "../common/license.h"
33 // The new (T)itan(C)overage(D)ata is born!
34 const std::string
tcd_ext(".tcd");
35 size_t tcd_ext_len
= tcd_ext
.size();
43 void TcovData::inc_function(const std::string
& function
, int n
)
45 if (m_functions
.count(function
) == 0) m_functions
[function
] = 0;
46 m_functions
[function
] += n
;
49 void TcovData::inc_line(int line
, int n
)
51 if (m_lines
.count(line
) == 0) m_lines
[line
] = 0;
55 Tcov2Lcov::Tcov2Lcov(const char *code_base
, const char *input_dir
, const char *output_file
, const char *xsd_file
)
56 : m_code_base(code_base
), m_input_dir(input_dir
), m_output_file(output_file
), m_xsd_file(xsd_file
)
58 m_ver_major
= m_ver_minor
= -1;
61 Tcov2Lcov::~Tcov2Lcov()
63 std::map
<std::string
, TcovData
*>::iterator i
= m_data
.begin();
64 for (; i
!= m_data
.end(); ++i
) {
70 int Tcov2Lcov::collect_dir(std::string dir
)
73 if ((d
= opendir(dir
.c_str())) == NULL
) {
74 // Fatal for top-level directory.
75 std::cerr
<< "Error while opening directory `" << dir
<< "'" << std::endl
;
78 struct dirent
*de
= NULL
;
79 while ((de
= readdir(d
)) != NULL
) {
80 std::string
file_name(de
->d_name
);
81 std::string full_file_name
= dir
+ P_SEP
+ file_name
;
82 // No `d_type' field on Solaris...
84 int stat_ret_val
= stat(de
->d_name
, &st
);
85 if (stat_ret_val
!= 0)
87 if (S_ISDIR(st
.st_mode
)) {
88 if (file_name
== ".." || file_name
== ".")
90 collect_dir(full_file_name
);
92 size_t file_name_len
= file_name
.size();
93 if (file_name_len
> tcd_ext_len
94 && file_name
.substr(file_name_len
- tcd_ext_len
) == tcd_ext
) {
95 m_files
.push_back(full_file_name
);
103 int Tcov2Lcov::collect()
105 if (collect_dir(m_input_dir
) < 0)
107 return static_cast<int>(m_files
.size());
110 void Tcov2Lcov::d_print_files() const
112 std::cout
<< "Collected XML/TCD files:" << std::endl
;
113 for (size_t i
= 0; i
< m_files
.size(); ++i
)
114 std::cout
<< "#" << i
<< ": " << m_files
[i
] << std::endl
;
117 int Tcov2Lcov::validate() const
120 xmlLineNumbersDefault(1);
122 xmlSchemaParserCtxtPtr ctxt
= xmlSchemaNewParserCtxt(m_xsd_file
.c_str());
123 xmlSchemaSetParserErrors(ctxt
, (xmlSchemaValidityErrorFunc
)fprintf
,
124 (xmlSchemaValidityWarningFunc
)fprintf
, stderr
);
125 // valgrind reports `Conditional jump or move depends on uninitialised
126 // value(s)' even for xmllint when using xmlSchemaParse().
127 xmlSchemaPtr schema
= xmlSchemaParse(ctxt
);
128 xmlSchemaFreeParserCtxt(ctxt
);
129 if (schema
== NULL
) {
130 xmlSchemaCleanupTypes();
136 for (size_t i
= 0; i
< m_files
.size(); ++i
) {
137 const char *xml_file_name
= m_files
[i
].c_str();
138 xmlDocPtr doc
= xmlReadFile(xml_file_name
, NULL
, 0);
140 std::cerr
<< "Could not parse `" << xml_file_name
<< "'" << std::endl
;
144 xmlSchemaValidCtxtPtr xml_ctxt
= xmlSchemaNewValidCtxt(schema
);
145 xmlSchemaSetValidErrors(xml_ctxt
, (xmlSchemaValidityErrorFunc
)fprintf
,
146 (xmlSchemaValidityWarningFunc
)fprintf
, stderr
);
147 int valid
= xmlSchemaValidateDoc(xml_ctxt
, doc
);
149 std::cerr
<< "`" << xml_file_name
<< "' fails to validate" << std::endl
;
152 xmlSchemaFreeValidCtxt(xml_ctxt
);
156 xmlSchemaFree(schema
);
157 xmlSchemaCleanupTypes();
163 const char *get_next_attr(const xmlTextReaderPtr
& reader
)
165 xmlTextReaderMoveToNextAttribute(reader
);
166 return (const char *)xmlTextReaderConstValue(reader
);
169 int Tcov2Lcov::merge()
171 for (size_t i
= 0; i
< m_files
.size(); ++i
) {
172 std::string
& file_name
= m_files
[i
];
173 xmlInitParser(); // Avoid weird memory leaks...
174 xmlTextReaderPtr reader
= xmlReaderForFile(file_name
.c_str(), NULL
, 0);
175 if (reader
== NULL
) {
176 std::cerr
<< "Unable to open `" << file_name
<< "'" << std::endl
;
179 std::string current_file_name
;
180 while (xmlTextReaderRead(reader
)) {
181 switch (xmlTextReaderNodeType(reader
)) {
182 case XML_READER_TYPE_ELEMENT
: {
183 std::string
elem((const char *)xmlTextReaderConstName(reader
));
184 if (elem
== "version") {
185 int major
= atoi(get_next_attr(reader
));
186 int minor
= atoi(get_next_attr(reader
));
187 if (m_ver_major
== -1) m_ver_major
= major
;
188 if (m_ver_minor
== -1) m_ver_minor
= minor
;
189 if (m_ver_major
!= major
|| m_ver_minor
!= minor
) {
190 std::cerr
<< "Version mismatch in `" << file_name
<< "' ("
191 << major
<< "." << minor
<< " vs. " << m_ver_major
192 << "." << m_ver_minor
<< ")" << std::endl
;
193 xmlFreeTextReader(reader
);
198 } else if (elem
== "file") {
199 current_file_name
= get_next_attr(reader
);
200 if (m_data
.count(current_file_name
) == 0)
201 m_data
[current_file_name
] = new TcovData();
202 } else if (elem
== "function") {
203 std::string
function_name(get_next_attr(reader
));
204 int function_count
= atoi(get_next_attr(reader
));
205 m_data
[current_file_name
]->inc_function(function_name
, function_count
);
206 } else if (elem
== "line") {
207 int line_no
= atoi(get_next_attr(reader
));
208 int line_count
= atoi(get_next_attr(reader
));
209 m_data
[current_file_name
]->inc_line(line_no
, line_count
);
211 xmlTextReaderMoveToElement(reader
);
219 xmlFreeTextReader(reader
);
226 int Tcov2Lcov::generate()
229 if (!(stat(m_code_base
.c_str(), &st
) == 0 && S_ISDIR(st
.st_mode
))) {
230 std::cerr
<< "Invalid code base `" << m_code_base
<< "'" << std::endl
;
233 std::ofstream
output(m_output_file
.c_str());
234 if (!output
.is_open()) {
235 std::cerr
<< "Output file `" << m_output_file
<< "' cannot be opened" << std::endl
;
238 output
<< "TN:" << std::endl
;
239 std::map
<std::string
, TcovData
*>::const_iterator i
= m_data
.begin();
240 for (; i
!= m_data
.end(); ++i
) {
241 output
<< "SF:" << m_code_base
<< P_SEP
<< (*i
).first
<< std::endl
;
242 const TcovData
*data
= (*i
).second
;
243 const std::map
<std::string
, int>& functions
= data
->get_functions();
244 const std::map
<int, int>& lines
= data
->get_lines();
245 std::map
<std::string
, int>::const_iterator j
= functions
.begin();
246 std::map
<int, int>::const_iterator k
= lines
.begin();
247 for (; j
!= functions
.end(); ++j
) // All functions.
248 output
<< "FN:0," << (*j
).first
<< std::endl
;
249 for (j
= functions
.begin(); j
!= functions
.end(); ++j
) // All functions counted.
250 output
<< "FNDA:" << (*j
).second
<< "," << (*j
).first
<< std::endl
;
251 for (; k
!= lines
.end(); ++k
) // All lines counted.
252 output
<< "DA:" << (*k
).first
<< "," << (*k
).second
<< std::endl
;
253 output
<< "end_of_record" << std::endl
;
259 static void print_product_info()
261 std::cerr
<< "TCD to LCOV Converter for the TTCN-3 Test Executor, version "
262 << PRODUCT_NUMBER
<< std::endl
;
265 static void print_version()
267 std::cerr
<< "Product number: " << PRODUCT_NUMBER
<< std::endl
268 << "Build date: " << __DATE__
<< " " << __TIME__
<< std::endl
269 << "Compiled with: " << C_COMPILER_VERSION
<< std::endl
<< std::endl
270 << COPYRIGHT_STRING
<< std::endl
<< std::endl
;
273 static void print_help(const char *name
)
275 std::cerr
<< "Usage: " << name
<< " [-hv] [-b dir] [-d dir] [-o file] [-x xsd] " << std::endl
276 << " -b dir\tSet code base for source files" << std::endl
277 << " -d dir\tCollect XML/TCD files from `dir'" << std::endl
278 << " -h\t\tPrint help" << std::endl
279 << " -o file\tPlace of the output file" << std::endl
280 << " -v\t\tShow version" << std::endl
281 << " -x xsd\tValidate all XMLs/TCDs against this `xsd'" << std::endl
;
284 int main(int argc
, char *argv
[])
286 // Unfortunately, getopt_long() and :: for optional parameters are GNU extensions and not present on Solaris...
287 // -b --dir: Set code base for source files (_optional_)
288 // -d --dir: Collect XML/TCD files from, default is `.' the current directory (_optional_)
289 // -o --output: Place of the output, default is `./output.info' (_optional_)
290 // -x --xsd: Validate all XMLs/TCDs against this XSD (_optional_)
291 // -h --help: Display help (_optional_)
292 const char *code_base
= NULL
, *input_dir
= NULL
, *output_file
= NULL
, *xsd_file
= NULL
;
294 while ((c
= getopt(argc
, argv
, "b:d:ho:vx:")) != -1) {
303 print_product_info();
307 output_file
= optarg
;
310 print_product_info();
313 print_license_info();
324 bool param_error
= false;
326 #define PATH_MAX 1024
329 memset(cwd
, 0, PATH_MAX
);
331 if (!getcwd(cwd
, PATH_MAX
)) {
332 std::cerr
<< "Unable to get current working directory for `-b'" << std::endl
;
334 } else code_base
= cwd
;
336 // Leave it relative.
337 if (!input_dir
) input_dir
= ".";
338 if (!output_file
) output_file
= "output.info";
339 std::string xsd_file_str
;
341 const char *ttcn3_dir
= getenv("TTCN3_DIR");
343 std::cerr
<< "$TTCN3_DIR environment variable is not set" << std::endl
;
346 xsd_file_str
= std::string(ttcn3_dir
) + P_SEP
+ std::string("include") + P_SEP
+ std::string("tcov.xsd");
347 xsd_file
= xsd_file_str
.c_str();
359 load_license (&lstr
);
360 int license_valid
= verify_license(&lstr
);
361 free_license (&lstr
);
363 if (!license_valid
) {
369 Tcov2Lcov
t2l(code_base
, input_dir
, output_file
, xsd_file
);
370 int num_files
= t2l
.collect();
372 std::cerr
<< "Directory `" << input_dir
<< "' doesn't exist" << std::endl
;
374 } else if (num_files
== 0) {
375 std::cerr
<< "No data files were found in the given directory `"
376 << input_dir
<< "'" << std::endl
;
379 // Initialize the library and check potential ABI mismatches between
380 // the between the version it was compiled for and the actual shared
383 if (t2l
.validate() < 0) {
384 std::cerr
<< "Error while validating XML/TCD files" << std::endl
;
387 if (t2l
.merge() < 0) {
388 std::cerr
<< "Error while merging XML/TCD files" << std::endl
;
391 if (t2l
.generate() < 0) {
392 std::cerr
<< "Error while generating output file `" << output_file
<< "'"
399 reffer::reffer(const char*) {}