File: test-read-common.cc

package info (click to toggle)
libabigail 2.9-2
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 1,021,756 kB
  • sloc: xml: 572,663; cpp: 110,945; sh: 11,868; ansic: 4,329; makefile: 3,486; python: 1,684; ada: 62
file content (266 lines) | stat: -rw-r--r-- 7,678 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// -*- 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);
  set_write_undefined_symbols(*write_ctxt, false);
  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;
  string spec_options = spec.options ? spec.options : "";
  set_in_abi_path();

  if (!in_public_headers_path.empty())
    drop_private_types += "--headers-dir " + in_public_headers_path +
      " --drop-private-types";
  string cmd = abidw + " " + spec_options + drop_private_types +
    " --abidiff " + extargs + in_elf_path;
  if (system(cmd.c_str()))
    {
      error_message = string("self comparison with abidw failed:\n")
	+ "command was: '" + cmd + "'\n";
      std::cerr << error_message;
      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("ABI files differ:\n")
        + in_abi_path
        + "\nand:\n"
        + out_abi_path
        + "\n"
	+ "command was: '" + cmd + "'\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