blob: 9681ac273ecff2617dd43d46f271b81758ed5a28 [file] [log] [blame]
// -*- Mode: C++ -*-
//
/// @file
///
/// This file implements the common functionality for the tests in
/// CTF and DWARF readers, it does the abstraction in the `act` test
/// stage.
#include <fstream>
#include <cstring>
#include "test-read-common.h"
using std::ofstream;
using std::cerr;
using std::dynamic_pointer_cast;
using abigail::tools_utils::emit_prefix;
using abigail::tests::get_build_dir;
using abigail::xml_writer::write_context_sptr;
using abigail::xml_writer::create_write_context;
using abigail::xml_writer::write_corpus;
namespace abigail
{
namespace tests
{
namespace read_common
{
/// Constructor.
///
/// Task to be executed for each test entry in @ref
/// abigail::tests::read_common::InOutSpec.
///
/// @param InOutSpec the set of tests.
///
/// @param a_out_abi_base the output base directory for abixml files.
///
/// @param a_in_elf_base the input base directory for object files.
///
/// @param a_in_elf_base the input base directory for expected
/// abixml files.
test_task::test_task(const InOutSpec &s,
string& a_out_abi_base,
string& a_in_elf_base,
string& a_in_abi_base)
: is_ok(true),
spec(s),
out_abi_base(a_out_abi_base),
in_elf_base(a_in_elf_base),
in_abi_base(a_in_abi_base)
{}
/// Serialize the abixml @p out_abi_path file.
///
/// @param out_abi_path the abixml path file.
///
/// @param corp the ABI @ref abigail::ir::corpus.
///
/// @return true if abixml file was serialized successfully. Otherwise
/// `error_message` is set with @p out_abi_path and false is returned.
bool
test_task::serialize_corpus(const string& out_abi_path,
corpus_sptr corp)
{
ofstream of(out_abi_path.c_str(), std::ios_base::trunc);
if (!of.is_open())
{
error_message = string("failed to read ") + out_abi_path + "\n";
return false;
}
write_context_sptr write_ctxt
= create_write_context(corp->get_environment(), of);
set_type_id_style(*write_ctxt, spec.type_id_style);
is_ok = write_corpus(*write_ctxt, corp, /*indent=*/0);
of.close();
return is_ok;
}
/// Spawn `abidw --abidiff` tool appending @p extargs argument.
///
/// Thew input file object used by `abidw` will be specified by
/// `in_elf_path'.
///
/// @param extargs the extra argument(s) passed to `abidw` tool.
///
/// @return true if `abidw` tool was executed correctly. Otherwise
/// `error_message` shows the full path of the input file object and
/// the full output path for the abixml file.
bool
test_task::run_abidw(const string& extargs)
{
string abidw = string(get_build_dir()) + "/tools/abidw";
string drop_private_types;
if (!in_public_headers_path.empty())
drop_private_types += "--headers-dir " + in_public_headers_path +
" --drop-private-types";
string cmd = abidw + " " + drop_private_types + " --abidiff " + extargs +
in_elf_path;
if (system(cmd.c_str()))
{
error_message = string("ABIs differ:\n")
+ in_elf_path
+ "\nand:\n"
+ out_abi_path
+ "\n";
return false;
}
return true;
}
/// Spawn external `diff` command.
///
/// The files to be compared are: abixml generated by the input
/// object file and the expected abixml file stored in `in_abi_path`.
///
/// @return true if `diff` command didn't find defences. Otherwise
/// `error_message` shows the full path of the input file object and
/// the full output path for the abixml file.
bool
test_task::run_diff()
{
set_in_abi_path();
string cmd = "diff -u " + in_abi_path + " " + out_abi_path;
if (system(cmd.c_str()))
{
error_message = string("ABIs differ:\n")
+ in_abi_path
+ "\nand:\n"
+ out_abi_path
+ "\n";
return false;
}
return true;
}
/// Write the usage message to @p out stream object.
///
/// @param prog_name the program name.
///
/// @param out the stream object to which want to write.
void
display_usage(const string& prog_name, ostream& out)
{
emit_prefix(prog_name, out)
<< "usage: " << prog_name << " [options]\n"
<< " where options can be: \n"
<< " --help|-h display this message\n"
<< " --no-parallel execute testsuite is a sigle thread\n"
;
}
/// Parse and process test options.
///
/// @param argc the arguments number.
///
/// @param argv the pointer to the arguments.
///
/// @param opts the valid @ref options to be processed/parsed.
///
/// @return true if options was processed/parsed successfully. It returns
/// false when help is requested or an invalid option is supplied.
bool
parse_command_line(int argc, char* argv[], options& opts)
{
for (int i = 1; i < argc; ++i)
{
if (!strcmp(argv[i], "--no-parallel"))
opts.parallel = false;
else if (!strcmp(argv[i], "--help")
|| !strcmp(argv[i], "--h"))
return false;
else
{
if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
opts.wrong_option = argv[i];
return false;
}
}
return true;
}
/// The main entry point to execute the testsuite.
///
/// @param num_tests the number of tests to be executed.
///
/// @param specs the @ref abigail::tests::read_common::InOutSpec
/// tests container.
///
/// @param opts the test execution @ref abigail::tests::read_common::options.
///
/// @param new_test the @ref create_new_test callback function to create
/// a new test task object.
///
/// @return true if `all` tests were performed successfully. Otherwise
/// false is returned.
bool
run_tests(const size_t num_tests, const InOutSpec* specs,
const options& opts, create_new_test new_test)
{
size_t num_workers = (opts.parallel
? std::min(abigail::workers::get_number_of_threads(),
num_tests)
: 1);
// Create a task queue. The max number of worker threads of the
// queue is the number of the concurrent threads supported by the
// processor of the machine this code runs on. But if
// --no-parallel was provided then the number of worker threads
// equals 1.
abigail::workers::queue task_queue(num_workers);
bool is_ok = true;
string out_abi_base = string(get_build_dir()) + "/tests/";
string in_elf_base = string(abigail::tests::get_src_dir()) + "/tests/";
string in_abi_base = in_elf_base;
for (const InOutSpec *s = specs; s->in_elf_path; ++s)
{
test_task_sptr t(new_test(s, out_abi_base,
in_elf_base,
in_abi_base));
ABG_ASSERT(task_queue.schedule_task(t));
}
// Wait for all worker threads to finish their job, and wind down.
task_queue.wait_for_workers_to_complete();
// Now walk the results and print whatever error messages need to be
// printed.
const vector<abigail::workers::task_sptr>& completed_tasks =
task_queue.get_completed_tasks();
ABG_ASSERT(completed_tasks.size() == num_tests);
for (vector<abigail::workers::task_sptr>::const_iterator ti =
completed_tasks.begin();
ti != completed_tasks.end();
++ti)
{
test_task_sptr t = dynamic_pointer_cast<test_task>(*ti);
if (!t->is_ok)
{
is_ok = false;
if (!t->error_message.empty())
cerr << t->error_message << '\n';
}
}
return !is_ok;
}
}//end namespace read_common
}//end namespace tests
}//end namespace abigail