• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // -*- Mode: C++ -*-
2 //
3 
4 /// @file
5 ///
6 /// This file implements the common functionality for the tests in
7 /// CTF and DWARF readers, it does the abstraction in the `act` test
8 /// stage.
9 
10 #include <fstream>
11 #include <cstring>
12 #include "test-read-common.h"
13 
14 using std::ofstream;
15 using std::cerr;
16 using std::dynamic_pointer_cast;
17 
18 using abigail::tools_utils::emit_prefix;
19 using abigail::tests::get_build_dir;
20 using abigail::xml_writer::write_context_sptr;
21 using abigail::xml_writer::create_write_context;
22 using abigail::xml_writer::write_corpus;
23 
24 namespace abigail
25 {
26 namespace tests
27 {
28 namespace read_common
29 {
30 
31 /// Constructor.
32 ///
33 /// Task to be executed for each test entry in @ref
34 /// abigail::tests::read_common::InOutSpec.
35 ///
36 /// @param InOutSpec the set of tests.
37 ///
38 /// @param a_out_abi_base the output base directory for abixml files.
39 ///
40 /// @param a_in_elf_base the input base directory for object files.
41 ///
42 /// @param a_in_elf_base the input base directory for expected
43 /// abixml files.
test_task(const InOutSpec & s,string & a_out_abi_base,string & a_in_elf_base,string & a_in_abi_base)44 test_task::test_task(const InOutSpec &s,
45                      string& a_out_abi_base,
46                      string& a_in_elf_base,
47                      string& a_in_abi_base)
48     : is_ok(true),
49       spec(s),
50       out_abi_base(a_out_abi_base),
51       in_elf_base(a_in_elf_base),
52       in_abi_base(a_in_abi_base)
53   {}
54 
55 /// Serialize the abixml @p out_abi_path file.
56 ///
57 /// @param out_abi_path the abixml path file.
58 ///
59 /// @param corp the ABI @ref abigail::ir::corpus.
60 ///
61 /// @return true if abixml file was serialized successfully. Otherwise
62 /// `error_message` is set with @p out_abi_path and false is returned.
63 bool
serialize_corpus(const string & out_abi_path,corpus_sptr corp)64 test_task::serialize_corpus(const string& out_abi_path,
65                             corpus_sptr corp)
66 {
67   ofstream of(out_abi_path.c_str(), std::ios_base::trunc);
68   if (!of.is_open())
69     {
70        error_message = string("failed to read ") + out_abi_path + "\n";
71        return false;
72     }
73 
74   write_context_sptr write_ctxt
75       = create_write_context(corp->get_environment(), of);
76   set_type_id_style(*write_ctxt, spec.type_id_style);
77   is_ok = write_corpus(*write_ctxt, corp, /*indent=*/0);
78   of.close();
79 
80   return is_ok;
81 }
82 
83 /// Spawn `abidw --abidiff` tool appending @p extargs argument.
84 ///
85 /// Thew input file object used by `abidw` will be specified by
86 /// `in_elf_path'.
87 ///
88 /// @param extargs the extra argument(s) passed to `abidw` tool.
89 ///
90 /// @return true if `abidw` tool was executed correctly. Otherwise
91 /// `error_message` shows the full path of the input file object and
92 /// the full output path for the abixml file.
93 bool
run_abidw(const string & extargs)94 test_task::run_abidw(const string& extargs)
95 {
96   string abidw = string(get_build_dir()) + "/tools/abidw";
97   string drop_private_types;
98   if (!in_public_headers_path.empty())
99     drop_private_types += "--headers-dir " + in_public_headers_path +
100       " --drop-private-types";
101   string cmd = abidw + " " + drop_private_types + " --abidiff " + extargs +
102    in_elf_path;
103   if (system(cmd.c_str()))
104     {
105       error_message = string("ABIs differ:\n")
106         + in_elf_path
107         + "\nand:\n"
108         + out_abi_path
109         + "\n";
110 
111       return false;
112     }
113 
114   return true;
115 }
116 
117 /// Spawn external `diff` command.
118 ///
119 /// The files to be compared are: abixml generated by the input
120 /// object file and the expected abixml file stored in `in_abi_path`.
121 ///
122 /// @return true if `diff` command didn't find defences. Otherwise
123 /// `error_message` shows the full path of the input file object and
124 /// the full output path for the abixml file.
125 bool
run_diff()126 test_task::run_diff()
127 {
128   set_in_abi_path();
129   string cmd = "diff -u " + in_abi_path + " " + out_abi_path;
130   if (system(cmd.c_str()))
131     {
132       error_message = string("ABIs differ:\n")
133         + in_abi_path
134         + "\nand:\n"
135         + out_abi_path
136         + "\n";
137 
138       return false;
139     }
140 
141   return true;
142 }
143 
144 /// Write the usage message to @p out stream object.
145 ///
146 /// @param prog_name the program name.
147 ///
148 /// @param out the stream object to which want to write.
149 void
display_usage(const string & prog_name,ostream & out)150 display_usage(const string& prog_name, ostream& out)
151 {
152   emit_prefix(prog_name, out)
153     << "usage: " << prog_name << " [options]\n"
154     << " where options can be: \n"
155     << "  --help|-h  display this message\n"
156     << "  --no-parallel execute testsuite is a sigle thread\n"
157   ;
158 }
159 
160 /// Parse and process test options.
161 ///
162 /// @param argc the arguments number.
163 ///
164 /// @param argv the pointer to the arguments.
165 ///
166 /// @param opts the valid @ref options to be processed/parsed.
167 ///
168 /// @return true if options was processed/parsed successfully. It returns
169 /// false when help is requested or an invalid option is supplied.
170 bool
parse_command_line(int argc,char * argv[],options & opts)171 parse_command_line(int argc, char* argv[], options& opts)
172 {
173   for (int i = 1; i < argc; ++i)
174     {
175       if (!strcmp(argv[i], "--no-parallel"))
176         opts.parallel = false;
177       else if (!strcmp(argv[i], "--help")
178                || !strcmp(argv[i], "--h"))
179         return false;
180       else
181         {
182           if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
183             opts.wrong_option = argv[i];
184           return false;
185         }
186     }
187 
188   return true;
189 }
190 
191 /// The main entry point to execute the testsuite.
192 ///
193 /// @param num_tests the number of tests to be executed.
194 ///
195 /// @param specs the @ref abigail::tests::read_common::InOutSpec
196 /// tests container.
197 ///
198 /// @param opts the test execution @ref abigail::tests::read_common::options.
199 ///
200 /// @param new_test the @ref create_new_test callback function to create
201 /// a new test task object.
202 ///
203 /// @return true if `all` tests were performed successfully. Otherwise
204 /// false is returned.
205 bool
run_tests(const size_t num_tests,const InOutSpec * specs,const options & opts,create_new_test new_test)206 run_tests(const size_t num_tests, const InOutSpec* specs,
207           const options& opts, create_new_test new_test)
208 {
209   size_t num_workers = (opts.parallel
210                         ? std::min(abigail::workers::get_number_of_threads(),
211                                    num_tests)
212                         : 1);
213 
214   // Create a task queue.  The max number of worker threads of the
215   // queue is the number of the concurrent threads supported by the
216   // processor of the machine this code runs on.  But if
217   // --no-parallel was provided then the number of worker threads
218   // equals 1.
219   abigail::workers::queue task_queue(num_workers);
220   bool is_ok = true;
221 
222   string out_abi_base = string(get_build_dir()) + "/tests/";
223   string in_elf_base  = string(abigail::tests::get_src_dir()) + "/tests/";
224   string in_abi_base = in_elf_base;
225 
226   for (const InOutSpec *s = specs; s->in_elf_path; ++s)
227     {
228       test_task_sptr t(new_test(s, out_abi_base,
229                                 in_elf_base,
230                                 in_abi_base));
231       ABG_ASSERT(task_queue.schedule_task(t));
232     }
233 
234   // Wait for all worker threads to finish their job, and wind down.
235   task_queue.wait_for_workers_to_complete();
236 
237   // Now walk the results and print whatever error messages need to be
238   // printed.
239 
240   const vector<abigail::workers::task_sptr>& completed_tasks =
241     task_queue.get_completed_tasks();
242 
243   ABG_ASSERT(completed_tasks.size() == num_tests);
244 
245   for (vector<abigail::workers::task_sptr>::const_iterator ti =
246          completed_tasks.begin();
247        ti != completed_tasks.end();
248        ++ti)
249     {
250       test_task_sptr t = dynamic_pointer_cast<test_task>(*ti);
251       if (!t->is_ok)
252         {
253           is_ok = false;
254           if (!t->error_message.empty())
255             cerr << t->error_message << '\n';
256         }
257     }
258 
259   return !is_ok;
260 }
261 
262 }//end namespace read_common
263 }//end namespace tests
264 }//end namespace abigail
265