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