libabigail
test-read-common.cc
Go to the documentation of this file.
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 
19 using abigail::tests::get_build_dir;
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.
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
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  set_write_undefined_symbols(*write_ctxt, false);
78  is_ok = write_corpus(*write_ctxt, corp, /*indent=*/0);
79  of.close();
80 
81  return is_ok;
82 }
83 
84 /// Spawn `abidw --abidiff` tool appending @p extargs argument.
85 ///
86 /// Thew input file object used by `abidw` will be specified by
87 /// `in_elf_path'.
88 ///
89 /// @param extargs the extra argument(s) passed to `abidw` tool.
90 ///
91 /// @return true if `abidw` tool was executed correctly. Otherwise
92 /// `error_message` shows the full path of the input file object and
93 /// the full output path for the abixml file.
94 bool
95 test_task::run_abidw(const string& extargs)
96 {
97  string abidw = string(get_build_dir()) + "/tools/abidw";
98  string drop_private_types;
99  string spec_options = spec.options ? spec.options : "";
100  set_in_abi_path();
101 
102  if (!in_public_headers_path.empty())
103  drop_private_types += "--headers-dir " + in_public_headers_path +
104  " --drop-private-types";
105  string cmd = abidw + " " + spec_options + drop_private_types +
106  " --abidiff " + extargs + in_elf_path;
107  if (system(cmd.c_str()))
108  {
109  error_message = string("ABIs differ:\n")
110  + in_abi_path
111  + "\nand:\n"
112  + out_abi_path
113  + "\n"
114  + "command was: '" + cmd + "'\n";
115 
116  return false;
117  }
118 
119  return true;
120 }
121 
122 /// Spawn external `diff` command.
123 ///
124 /// The files to be compared are: abixml generated by the input
125 /// object file and the expected abixml file stored in `in_abi_path`.
126 ///
127 /// @return true if `diff` command didn't find defences. Otherwise
128 /// `error_message` shows the full path of the input file object and
129 /// the full output path for the abixml file.
130 bool
132 {
133  set_in_abi_path();
134  string cmd = "diff -u " + in_abi_path + " " + out_abi_path;
135  if (system(cmd.c_str()))
136  {
137  error_message = string("ABI files differ:\n")
138  + in_abi_path
139  + "\nand:\n"
140  + out_abi_path
141  + "\n"
142  + "command was: '" + cmd + "'\n";
143 
144  return false;
145  }
146 
147  return true;
148 }
149 
150 /// Write the usage message to @p out stream object.
151 ///
152 /// @param prog_name the program name.
153 ///
154 /// @param out the stream object to which want to write.
155 void
156 display_usage(const string& prog_name, ostream& out)
157 {
158  emit_prefix(prog_name, out)
159  << "usage: " << prog_name << " [options]\n"
160  << " where options can be: \n"
161  << " --help|-h display this message\n"
162  << " --no-parallel execute testsuite is a sigle thread\n"
163  ;
164 }
165 
166 /// Parse and process test options.
167 ///
168 /// @param argc the arguments number.
169 ///
170 /// @param argv the pointer to the arguments.
171 ///
172 /// @param opts the valid @ref options to be processed/parsed.
173 ///
174 /// @return true if options was processed/parsed successfully. It returns
175 /// false when help is requested or an invalid option is supplied.
176 bool
177 parse_command_line(int argc, char* argv[], options& opts)
178 {
179  for (int i = 1; i < argc; ++i)
180  {
181  if (!strcmp(argv[i], "--no-parallel"))
182  opts.parallel = false;
183  else if (!strcmp(argv[i], "--help")
184  || !strcmp(argv[i], "--h"))
185  return false;
186  else
187  {
188  if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
189  opts.wrong_option = argv[i];
190  return false;
191  }
192  }
193 
194  return true;
195 }
196 
197 /// The main entry point to execute the testsuite.
198 ///
199 /// @param num_tests the number of tests to be executed.
200 ///
201 /// @param specs the @ref abigail::tests::read_common::InOutSpec
202 /// tests container.
203 ///
204 /// @param opts the test execution @ref abigail::tests::read_common::options.
205 ///
206 /// @param new_test the @ref create_new_test callback function to create
207 /// a new test task object.
208 ///
209 /// @return true if `all` tests were performed successfully. Otherwise
210 /// false is returned.
211 bool
212 run_tests(const size_t num_tests, const InOutSpec* specs,
213  const options& opts, create_new_test new_test)
214 {
215  size_t num_workers = (opts.parallel
217  num_tests)
218  : 1);
219 
220  // Create a task queue. The max number of worker threads of the
221  // queue is the number of the concurrent threads supported by the
222  // processor of the machine this code runs on. But if
223  // --no-parallel was provided then the number of worker threads
224  // equals 1.
225  abigail::workers::queue task_queue(num_workers);
226  bool is_ok = true;
227 
228  string out_abi_base = string(get_build_dir()) + "/tests/";
229  string in_elf_base = string(abigail::tests::get_src_dir()) + "/tests/";
230  string in_abi_base = in_elf_base;
231 
232  for (const InOutSpec *s = specs; s->in_elf_path; ++s)
233  {
234  test_task_sptr t(new_test(s, out_abi_base,
235  in_elf_base,
236  in_abi_base));
237  ABG_ASSERT(task_queue.schedule_task(t));
238  }
239 
240  // Wait for all worker threads to finish their job, and wind down.
241  task_queue.wait_for_workers_to_complete();
242 
243  // Now walk the results and print whatever error messages need to be
244  // printed.
245 
246  const vector<abigail::workers::task_sptr>& completed_tasks =
247  task_queue.get_completed_tasks();
248 
249  ABG_ASSERT(completed_tasks.size() == num_tests);
250 
251  for (vector<abigail::workers::task_sptr>::const_iterator ti =
252  completed_tasks.begin();
253  ti != completed_tasks.end();
254  ++ti)
255  {
256  test_task_sptr t = dynamic_pointer_cast<test_task>(*ti);
257  if (!t->is_ok)
258  {
259  is_ok = false;
260  if (!t->error_message.empty())
261  cerr << t->error_message << '\n';
262  }
263  }
264 
265  return !is_ok;
266 }
267 
268 }//end namespace read_common
269 }//end namespace tests
270 }//end namespace abigail
#define ABG_ASSERT(cond)
This is a wrapper around the 'assert' glibc call. It allows for its argument to have side effects,...
Definition: abg-fwd.h:1714
This represents a queue of tasks to be performed.
Definition: abg-workers.h:68
tasks_type & get_completed_tasks() const
Getter of the vector of tasks that got performed.
Definition: abg-workers.cc:347
void wait_for_workers_to_complete()
Suspends the current thread until all worker threads finish performing the tasks they are executing.
Definition: abg-workers.cc:340
bool schedule_task(const task_sptr &)
Submit a task to the queue of tasks to be performed.
Definition: abg-workers.cc:315
ostream & emit_prefix(const string &prog_name, ostream &out)
Emit a prefix made of the name of the program which is emitting a message to an output stream.
size_t get_number_of_threads()
Definition: abg-workers.cc:75
void set_write_undefined_symbols(write_context &ctxt, bool flag)
Set the 'undefined-symbols' flag.
Definition: abg-writer.cc:2334
write_context_sptr create_write_context(const environment &env, ostream &default_output_stream)
Create a write_context object that can be used to emit abixml files.
Definition: abg-writer.cc:2210
shared_ptr< write_context > write_context_sptr
A convenience typedef for a shared pointer to write_context.
Definition: abg-writer.h:33
bool write_corpus(write_context &ctxt, const corpus_sptr &corpus, unsigned indent, bool member_of_group)
Serialize an ABI corpus to a single native xml document. The root note of the resulting XML document ...
Definition: abg-writer.cc:4756
void set_type_id_style(write_context &ctxt, type_id_style_kind style)
Set the 'type-id-style' property.
Definition: abg-writer.cc:2360
Toplevel namespace for libabigail.
This is an aggregate that specifies where a test shall get its input from, and where it shall write i...
An abstraction for valid test options.
bool serialize_corpus(const string &out_abi_path, corpus_sptr corp)
Serialize the abixml out_abi_path file.
test_task(const InOutSpec &s, string &a_out_abi_base, string &a_in_elf_base, string &a_in_abi_base)
Constructor.
void set_in_abi_path()
A setter for `in_abi_path` field. The `in_abi_path` is the full path for the expected abixml file.
bool run_abidw(const string &extargs="")
Spawn `abidw –abidiff` tool appending extargs argument.
bool run_diff()
Spawn external `diff` command.
bool run_tests(const size_t num_tests, const InOutSpec *specs, const options &opts, create_new_test new_test)
The main entry point to execute the testsuite.
bool parse_command_line(int argc, char *argv[], options &opts)
Parse and process test options.
void display_usage(const string &prog_name, ostream &out)
Write the usage message to out stream object.
This file declares the common functionality for tests in CTF and DWARF readers, it declares abstracti...