// -*- 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 #include #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& completed_tasks = task_queue.get_completed_tasks(); ABG_ASSERT(completed_tasks.size() == num_tests); for (vector::const_iterator ti = completed_tasks.begin(); ti != completed_tasks.end(); ++ti) { test_task_sptr t = dynamic_pointer_cast(*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