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