libabigail
Loading...
Searching...
No Matches
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
14using std::ofstream;
15using std::cerr;
16using std::dynamic_pointer_cast;
17
19using abigail::tests::get_build_dir;
23
24namespace abigail
25{
26namespace tests
27{
28namespace 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.
63bool
64test_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.
94bool
95test_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 : "";
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("self comparison with abidw failed:\n")
110 + "command was: '" + cmd + "'\n";
111 std::cerr << error_message;
112 return false;
113 }
114
115 return true;
116}
117
118/// Spawn external `diff` command.
119///
120/// The files to be compared are: abixml generated by the input
121/// object file and the expected abixml file stored in `in_abi_path`.
122///
123/// @return true if `diff` command didn't find defences. Otherwise
124/// `error_message` shows the full path of the input file object and
125/// the full output path for the abixml file.
126bool
128{
130 string cmd = "diff -u " + in_abi_path + " " + out_abi_path;
131 if (system(cmd.c_str()))
132 {
133 error_message = string("ABI files differ:\n")
134 + in_abi_path
135 + "\nand:\n"
136 + out_abi_path
137 + "\n"
138 + "command was: '" + cmd + "'\n";
139
140 return false;
141 }
142
143 return true;
144}
145
146/// Write the usage message to @p out stream object.
147///
148/// @param prog_name the program name.
149///
150/// @param out the stream object to which want to write.
151void
152display_usage(const string& prog_name, ostream& out)
153{
154 emit_prefix(prog_name, out)
155 << "usage: " << prog_name << " [options]\n"
156 << " where options can be: \n"
157 << " --help|-h display this message\n"
158 << " --no-parallel execute testsuite is a sigle thread\n"
159 ;
160}
161
162/// Parse and process test options.
163///
164/// @param argc the arguments number.
165///
166/// @param argv the pointer to the arguments.
167///
168/// @param opts the valid @ref options to be processed/parsed.
169///
170/// @return true if options was processed/parsed successfully. It returns
171/// false when help is requested or an invalid option is supplied.
172bool
173parse_command_line(int argc, char* argv[], options& opts)
174{
175 for (int i = 1; i < argc; ++i)
176 {
177 if (!strcmp(argv[i], "--no-parallel"))
178 opts.parallel = false;
179 else if (!strcmp(argv[i], "--help")
180 || !strcmp(argv[i], "--h"))
181 return false;
182 else
183 {
184 if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
185 opts.wrong_option = argv[i];
186 return false;
187 }
188 }
189
190 return true;
191}
192
193/// The main entry point to execute the testsuite.
194///
195/// @param num_tests the number of tests to be executed.
196///
197/// @param specs the @ref abigail::tests::read_common::InOutSpec
198/// tests container.
199///
200/// @param opts the test execution @ref abigail::tests::read_common::options.
201///
202/// @param new_test the @ref create_new_test callback function to create
203/// a new test task object.
204///
205/// @return true if `all` tests were performed successfully. Otherwise
206/// false is returned.
207bool
208run_tests(const size_t num_tests, const InOutSpec* specs,
209 const options& opts, create_new_test new_test)
210{
211 size_t num_workers = (opts.parallel
213 num_tests)
214 : 1);
215
216 // Create a task queue. The max number of worker threads of the
217 // queue is the number of the concurrent threads supported by the
218 // processor of the machine this code runs on. But if
219 // --no-parallel was provided then the number of worker threads
220 // equals 1.
221 abigail::workers::queue task_queue(num_workers);
222 bool is_ok = true;
223
224 string out_abi_base = string(get_build_dir()) + "/tests/";
225 string in_elf_base = string(abigail::tests::get_src_dir()) + "/tests/";
226 string in_abi_base = in_elf_base;
227
228 for (const InOutSpec *s = specs; s->in_elf_path; ++s)
229 {
230 test_task_sptr t(new_test(s, out_abi_base,
231 in_elf_base,
232 in_abi_base));
233 ABG_ASSERT(task_queue.schedule_task(t));
234 }
235
236 // Wait for all worker threads to finish their job, and wind down.
237 task_queue.wait_for_workers_to_complete();
238
239 // Now walk the results and print whatever error messages need to be
240 // printed.
241
242 const vector<abigail::workers::task_sptr>& completed_tasks =
243 task_queue.get_completed_tasks();
244
245 ABG_ASSERT(completed_tasks.size() == num_tests);
246
247 for (vector<abigail::workers::task_sptr>::const_iterator ti =
248 completed_tasks.begin();
249 ti != completed_tasks.end();
250 ++ti)
251 {
252 test_task_sptr t = dynamic_pointer_cast<test_task>(*ti);
253 if (!t->is_ok)
254 {
255 is_ok = false;
256 if (!t->error_message.empty())
257 cerr << t->error_message << '\n';
258 }
259 }
260
261 return !is_ok;
262}
263
264}//end namespace read_common
265}//end namespace tests
266}//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:1718
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.
void wait_for_workers_to_complete()
Suspends the current thread until all worker threads finish performing the tasks they are executing.
bool schedule_task(const task_sptr &)
Submit a task to the queue of tasks to be performed.
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()
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.
shared_ptr< write_context > write_context_sptr
A convenience typedef for a shared pointer to write_context.
Definition abg-writer.h:36
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 ...
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...