1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
2 // -*- Mode: C++ -*-
3 //
4 // Copyright (C) 2013-2020 Red Hat, Inc.
5
6 /// @file read an XML corpus file (in the native Abigail XML format),
7 /// save it back and diff the resulting XML file against the input
8 /// file. They should be identical.
9
10 #include <cstdlib>
11 #include <cstring>
12 #include <fstream>
13 #include <iostream>
14 #include <memory>
15 #include <string>
16 #include <vector>
17 #include "abg-ir.h"
18 #include "abg-reader.h"
19 #include "abg-writer.h"
20 #include "abg-workers.h"
21 #include "abg-tools-utils.h"
22 #include "test-utils.h"
23
24 using std::string;
25 using std::vector;
26 using std::ofstream;
27 using std::cerr;
28
29 using std::dynamic_pointer_cast;
30
31 using abigail::tools_utils::file_type;
32 using abigail::tools_utils::check_file;
33 using abigail::tools_utils::guess_file_type;
34 using abigail::tests::get_build_dir;
35 using abigail::ir::environment;
36 using abigail::ir::environment_sptr;
37 using abigail::translation_unit_sptr;
38 using abigail::corpus_sptr;
39 using abigail::xml_reader::read_translation_unit_from_file;
40 using abigail::xml_reader::read_corpus_from_native_xml_file;
41 using abigail::xml_writer::write_translation_unit;
42
43 using abigail::workers::queue;
44 using abigail::workers::task;
45 using abigail::workers::task_sptr;
46 using abigail::workers::get_number_of_threads;
47
48 /// This is an aggregate that specifies where a test shall get its
49 /// input from, and where it shall write its ouput to.
50 struct InOutSpec
51 {
52 const char* in_path;
53 const char* in_suppr_spec_path;
54 const char* ref_out_path;
55 const char* out_path;
56 };// end struct InOutSpec
57
58
59 InOutSpec in_out_specs[] =
60 {
61 {
62 "data/test-read-write/test0.xml",
63 "",
64 "data/test-read-write/test0.xml",
65 "output/test-read-write/test0.xml"
66 },
67 {
68 "data/test-read-write/test1.xml",
69 "",
70 "data/test-read-write/test1.xml",
71 "output/test-read-write/test1.xml"
72 },
73 {
74 "data/test-read-write/test2.xml",
75 "",
76 "data/test-read-write/test2.xml",
77 "output/test-read-write/test2.xml"
78 },
79 {
80 "data/test-read-write/test3.xml",
81 "",
82 "data/test-read-write/test3.xml",
83 "output/test-read-write/test3.xml"
84 },
85 {
86 "data/test-read-write/test4.xml",
87 "",
88 "data/test-read-write/test4.xml",
89 "output/test-read-write/test4.xml"
90 },
91 {
92 "data/test-read-write/test5.xml",
93 "",
94 "data/test-read-write/test5.xml",
95 "output/test-read-write/test5.xml"
96 },
97 {
98 "data/test-read-write/test6.xml",
99 "",
100 "data/test-read-write/test6.xml",
101 "output/test-read-write/test6.xml"
102 },
103 {
104 "data/test-read-write/test7.xml",
105 "",
106 "data/test-read-write/test7.xml",
107 "output/test-read-write/test7.xml"
108 },
109 {
110 "data/test-read-write/test8.xml",
111 "",
112 "data/test-read-write/test8.xml",
113 "output/test-read-write/test8.xml"
114 },
115 {
116 "data/test-read-write/test9.xml",
117 "",
118 "data/test-read-write/test9.xml",
119 "output/test-read-write/test9.xml"
120 },
121 {
122 "data/test-read-write/test10.xml",
123 "",
124 "data/test-read-write/test10.xml",
125 "output/test-read-write/test10.xml"
126 },
127 {
128 "data/test-read-write/test11.xml",
129 "",
130 "data/test-read-write/test11.xml",
131 "output/test-read-write/test11.xml"
132 },
133 {
134 "data/test-read-write/test12.xml",
135 "",
136 "data/test-read-write/test12.xml",
137 "output/test-read-write/test12.xml"
138 },
139 {
140 "data/test-read-write/test13.xml",
141 "",
142 "data/test-read-write/test13.xml",
143 "output/test-read-write/test13.xml"
144 },
145 {
146 "data/test-read-write/test14.xml",
147 "",
148 "data/test-read-write/test14.xml",
149 "output/test-read-write/test14.xml"
150 },
151 {
152 "data/test-read-write/test15.xml",
153 "",
154 "data/test-read-write/test15.xml",
155 "output/test-read-write/test15.xml"
156 },
157 {
158 "data/test-read-write/test16.xml",
159 "",
160 "data/test-read-write/test16.xml",
161 "output/test-read-write/test16.xml"
162 },
163 {
164 "data/test-read-write/test17.xml",
165 "",
166 "data/test-read-write/test17.xml",
167 "output/test-read-write/test17.xml"
168 },
169 {
170 "data/test-read-write/test18.xml",
171 "",
172 "data/test-read-write/test18.xml",
173 "output/test-read-write/test18.xml"
174 },
175 {
176 "data/test-read-write/test19.xml",
177 "",
178 "data/test-read-write/test19.xml",
179 "output/test-read-write/test19.xml"
180 },
181 {
182 "data/test-read-write/test20.xml",
183 "",
184 "data/test-read-write/test20.xml",
185 "output/test-read-write/test20.xml"
186 },
187 {
188 "data/test-read-write/test21.xml",
189 "",
190 "data/test-read-write/test21.xml",
191 "output/test-read-write/test21.xml"
192 },
193 {
194 "data/test-read-write/test22.xml",
195 "",
196 "data/test-read-write/test22.xml",
197 "output/test-read-write/test22.xml"
198 },
199 {
200 "data/test-read-write/test23.xml",
201 "",
202 "data/test-read-write/test23.xml",
203 "output/test-read-write/test23.xml"
204 },
205 {
206 "data/test-read-write/test24.xml",
207 "",
208 "data/test-read-write/test24.xml",
209 "output/test-read-write/test24.xml"
210 },
211 {
212 "data/test-read-write/test25.xml",
213 "",
214 "data/test-read-write/test25.xml",
215 "output/test-read-write/test25.xml"
216 },
217 {
218 "data/test-read-write/test26.xml",
219 "",
220 "data/test-read-write/test26.xml",
221 "output/test-read-write/test26.xml"
222 },
223 {
224 "data/test-read-write/test27.xml",
225 "",
226 "data/test-read-write/test27.xml",
227 "output/test-read-write/test27.xml"
228 },
229 {
230 "data/test-read-write/test28.xml",
231 "data/test-read-write/test28-drop-std-fns.abignore",
232 "data/test-read-write/test28-without-std-fns-ref.xml",
233 "output/test-read-write/test28-without-std-fns.xml"
234 },
235 {
236 "data/test-read-write/test28.xml",
237 "data/test-read-write/test28-drop-std-vars.abignore",
238 "data/test-read-write/test28-without-std-vars-ref.xml",
239 "output/test-read-write/test28-without-std-vars.xml"
240 },
241 {
242 "data/test-read-write/test-crc.xml",
243 "",
244 "data/test-read-write/test-crc.xml",
245 "output/test-read-write/test-crc.xml",
246 },
247 // This should be the last entry.
248 {NULL, NULL, NULL, NULL}
249 };
250
251 /// A task wihch reads an abixml file using abilint and compares its
252 /// output against a reference output.
253 struct test_task : public abigail::workers::task
254 {
255 InOutSpec spec;
256 bool is_ok;
257 string in_path, out_path, in_suppr_spec_path, ref_out_path;
258 string diff_cmd, error_message;
259
260 /// Constructor of the task.
261 ///
262 /// @param the spec of where to find the abixml file to read and the
263 /// reference output of the test.
test_tasktest_task264 test_task( InOutSpec& s)
265 : spec(s),
266 is_ok(true)
267 {}
268
269 /// This method defines what the task performs.
270 virtual void
performtest_task271 perform()
272 {
273 string input_suffix(spec.in_path);
274 in_path =
275 string(abigail::tests::get_src_dir()) + "/tests/" + input_suffix;
276
277 if (!check_file(in_path, cerr))
278 {
279 is_ok = false;
280 return;
281 }
282
283 string ref_out_path_suffix(spec.ref_out_path);
284 ref_out_path =
285 string(abigail::tests::get_src_dir())
286 + "/tests/" + ref_out_path_suffix;
287
288 if (!check_file(ref_out_path, cerr))
289 {
290 is_ok = false;
291 return;
292 }
293
294 if (spec.in_suppr_spec_path && strcmp(spec.in_suppr_spec_path, ""))
295 {
296 in_suppr_spec_path = string(spec.in_suppr_spec_path);
297 in_suppr_spec_path =
298 string(abigail::tests::get_src_dir())
299 + "/tests/"
300 + in_suppr_spec_path;
301 }
302 else
303 in_suppr_spec_path.clear();
304
305 environment_sptr env(new environment);
306 translation_unit_sptr tu;
307 corpus_sptr corpus;
308
309 file_type t = guess_file_type(in_path);
310 if (t == abigail::tools_utils::FILE_TYPE_UNKNOWN)
311 {
312 cerr << in_path << "is an unknown file type\n";
313 is_ok = false;
314 return;
315 }
316
317 string output_suffix(spec.out_path);
318 out_path =
319 string(abigail::tests::get_build_dir()) + "/tests/" + output_suffix;
320 if (!abigail::tools_utils::ensure_parent_dir_created(out_path))
321 {
322 error_message =
323 "Could not create parent director for " + out_path;
324 is_ok = false;
325 return;
326 }
327
328 string abilint = string(get_build_dir()) + "/tools/abilint";
329 if (!in_suppr_spec_path.empty())
330 abilint +=string(" --suppr ") + in_suppr_spec_path;
331 string cmd = abilint + " " + in_path + " > " + out_path;
332
333 if (system(cmd.c_str()))
334 {
335 error_message =
336 "ABI XML file doesn't pass abilint: " + out_path + "\n";
337 is_ok = false;
338 }
339
340 cmd = "diff -u " + ref_out_path + " " + out_path;
341 diff_cmd = cmd;
342 if (system(cmd.c_str()))
343 is_ok = false;
344 }
345 };// end struct test_task
346
347 /// A convenience typedef for shared
348 typedef shared_ptr<test_task> test_task_sptr;
349
350 /// Walk the array of InOutSpecs above, read the input files it points
351 /// to, write it into the output it points to and diff them.
352 int
main()353 main()
354 {
355 using abigail::workers::queue;
356 using abigail::workers::task;
357 using abigail::workers::task_sptr;
358 using abigail::workers::get_number_of_threads;
359
360 const size_t num_tests = sizeof(in_out_specs) / sizeof (InOutSpec) - 1;
361 size_t num_workers = std::min(get_number_of_threads(), num_tests);
362 queue task_queue(num_workers);
363
364 bool is_ok = true;
365
366
367 string in_path, out_path, in_suppr_spec_path, ref_out_path;
368 for (InOutSpec* s = in_out_specs; s->in_path; ++s)
369 {
370 test_task_sptr t(new test_task(*s));
371 ABG_ASSERT(task_queue.schedule_task(t));
372 }
373
374 /// Wait for all worker threads to finish their job, and wind down.
375 task_queue.wait_for_workers_to_complete();
376
377 // Now walk the results and print whatever error messages need to be
378 // printed.
379
380 const vector<task_sptr>& completed_tasks =
381 task_queue.get_completed_tasks();
382
383 ABG_ASSERT(completed_tasks.size() == num_tests);
384
385 for (vector<task_sptr>::const_iterator ti = completed_tasks.begin();
386 ti != completed_tasks.end();
387 ++ti)
388 {
389 test_task_sptr t = dynamic_pointer_cast<test_task>(*ti);
390 if (!t->is_ok)
391 {
392 is_ok = false;
393
394 if (!t->error_message.empty())
395 cerr << t->error_message << '\n';
396
397 if (!t->diff_cmd.empty())
398 if (system(t->diff_cmd.c_str()) == -1)
399 cerr << "execution of '" << t->diff_cmd << "' failed\n";
400 }
401 }
402
403 return !is_ok;
404 }
405