xref: /netbsd-src/external/gpl3/gcc/dist/gcc/selftest.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* A self-testing framework, for use by -fself-test.
2    Copyright (C) 2015-2022 Free Software Foundation, Inc.
3 
4 This file is part of GCC.
5 
6 GCC is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
9 version.
10 
11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3.  If not see
18 <http://www.gnu.org/licenses/>.  */
19 
20 #include "config.h"
21 #include "system.h"
22 #include "coretypes.h"
23 #include "selftest.h"
24 #include "intl.h"
25 
26 #if CHECKING_P
27 
28 namespace selftest {
29 
30 int num_passes;
31 
32 /* Record the successful outcome of some aspect of a test.  */
33 
34 void
pass(const location &,const char *)35 pass (const location &/*loc*/, const char */*msg*/)
36 {
37   num_passes++;
38 }
39 
40 /* Report the failed outcome of some aspect of a test and abort.  */
41 
42 void
fail(const location & loc,const char * msg)43 fail (const location &loc, const char *msg)
44 {
45   fprintf (stderr,"%s:%i: %s: FAIL: %s\n", loc.m_file, loc.m_line,
46 	   loc.m_function, msg);
47   abort ();
48 }
49 
50 /* As "fail", but using printf-style formatted output.  */
51 
52 void
fail_formatted(const location & loc,const char * fmt,...)53 fail_formatted (const location &loc, const char *fmt, ...)
54 {
55   va_list ap;
56 
57   fprintf (stderr, "%s:%i: %s: FAIL: ", loc.m_file, loc.m_line,
58 	   loc.m_function);
59   va_start (ap, fmt);
60   vfprintf (stderr, fmt, ap);
61   va_end (ap);
62   fprintf (stderr, "\n");
63   abort ();
64 }
65 
66 /* Implementation detail of ASSERT_STREQ.
67    Compare val1 and val2 with strcmp.  They ought
68    to be non-NULL; fail gracefully if either or both are NULL.  */
69 
70 void
assert_streq(const location & loc,const char * desc_val1,const char * desc_val2,const char * val1,const char * val2)71 assert_streq (const location &loc,
72 	      const char *desc_val1, const char *desc_val2,
73 	      const char *val1, const char *val2)
74 {
75   /* If val1 or val2 are NULL, fail with a custom error message.  */
76   if (val1 == NULL)
77     if (val2 == NULL)
78       fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=NULL",
79 		      desc_val1, desc_val2);
80     else
81       fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=\"%s\"",
82 		      desc_val1, desc_val2, val2);
83   else
84     if (val2 == NULL)
85       fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=\"%s\" val2=NULL",
86 		      desc_val1, desc_val2, val1);
87     else
88       {
89 	if (strcmp (val1, val2) == 0)
90 	  pass (loc, "ASSERT_STREQ");
91 	else
92 	  fail_formatted (loc, "ASSERT_STREQ (%s, %s)\n val1=\"%s\"\n val2=\"%s\"\n",
93 			  desc_val1, desc_val2, val1, val2);
94       }
95 }
96 
97 /* Implementation detail of ASSERT_STR_CONTAINS.
98    Use strstr to determine if val_needle is within val_haystack.
99    ::selftest::pass if it is found.
100    ::selftest::fail if it is not found.  */
101 
102 void
assert_str_contains(const location & loc,const char * desc_haystack,const char * desc_needle,const char * val_haystack,const char * val_needle)103 assert_str_contains (const location &loc,
104 		     const char *desc_haystack,
105 		     const char *desc_needle,
106 		     const char *val_haystack,
107 		     const char *val_needle)
108 {
109   /* If val_haystack is NULL, fail with a custom error message.  */
110   if (val_haystack == NULL)
111     fail_formatted (loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=NULL",
112 		    desc_haystack, desc_needle);
113 
114   /* If val_needle is NULL, fail with a custom error message.  */
115   if (val_needle == NULL)
116     fail_formatted (loc,
117 		    "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=NULL",
118 		    desc_haystack, desc_needle, val_haystack);
119 
120   const char *test = strstr (val_haystack, val_needle);
121   if (test)
122     pass (loc, "ASSERT_STR_CONTAINS");
123   else
124     fail_formatted
125 	(loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=\"%s\"",
126 	 desc_haystack, desc_needle, val_haystack, val_needle);
127 }
128 
129 /* Implementation detail of ASSERT_STR_STARTSWITH.
130    Determine if VAL_STR starts with VAL_PREFIX.
131    ::selftest::pass if VAL_STR does start with VAL_PREFIX.
132    ::selftest::fail if it does not, or either is NULL (using
133    DESC_STR and DESC_PREFIX in the error message).  */
134 
135 void
assert_str_startswith(const location & loc,const char * desc_str,const char * desc_prefix,const char * val_str,const char * val_prefix)136 assert_str_startswith (const location &loc,
137 		       const char *desc_str,
138 		       const char *desc_prefix,
139 		       const char *val_str,
140 		       const char *val_prefix)
141 {
142   /* If val_str is NULL, fail with a custom error message.  */
143   if (val_str == NULL)
144     fail_formatted (loc, "ASSERT_STR_STARTSWITH (%s, %s) str=NULL",
145 		    desc_str, desc_prefix);
146 
147   /* If val_prefix is NULL, fail with a custom error message.  */
148   if (val_prefix == NULL)
149     fail_formatted (loc,
150 		    "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=NULL",
151 		    desc_str, desc_prefix, val_str);
152 
153   if (startswith (val_str, val_prefix))
154     pass (loc, "ASSERT_STR_STARTSWITH");
155   else
156     fail_formatted
157 	(loc, "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=\"%s\"",
158 	 desc_str, desc_prefix, val_str, val_prefix);
159 }
160 
161 
162 /* Constructor.  Generate a name for the file.  */
163 
named_temp_file(const char * suffix)164 named_temp_file::named_temp_file (const char *suffix)
165 {
166   m_filename = make_temp_file (suffix);
167   ASSERT_NE (m_filename, NULL);
168 }
169 
170 /* Destructor.  Delete the tempfile.  */
171 
~named_temp_file()172 named_temp_file::~named_temp_file ()
173 {
174   unlink (m_filename);
175   diagnostics_file_cache_forcibly_evict_file (m_filename);
176   free (m_filename);
177 }
178 
179 /* Constructor.  Create a tempfile using SUFFIX, and write CONTENT to
180    it.  Abort if anything goes wrong, using LOC as the effective
181    location in the problem report.  */
182 
temp_source_file(const location & loc,const char * suffix,const char * content)183 temp_source_file::temp_source_file (const location &loc,
184 				    const char *suffix,
185 				    const char *content)
186 : named_temp_file (suffix)
187 {
188   FILE *out = fopen (get_filename (), "w");
189   if (!out)
190     fail_formatted (loc, "unable to open tempfile: %s", get_filename ());
191   fprintf (out, "%s", content);
192   fclose (out);
193 }
194 
195 /* As above, but with a size, to allow for NUL bytes in CONTENT.  */
196 
temp_source_file(const location & loc,const char * suffix,const char * content,size_t sz)197 temp_source_file::temp_source_file (const location &loc,
198 				    const char *suffix,
199 				    const char *content,
200 				    size_t sz)
201 : named_temp_file (suffix)
202 {
203   FILE *out = fopen (get_filename (), "w");
204   if (!out)
205     fail_formatted (loc, "unable to open tempfile: %s", get_filename ());
206   fwrite (content, sz, 1, out);
207   fclose (out);
208 }
209 
210 /* Avoid introducing locale-specific differences in the results
211    by hardcoding open_quote and close_quote.  */
212 
auto_fix_quotes()213 auto_fix_quotes::auto_fix_quotes ()
214 {
215   m_saved_open_quote = open_quote;
216   m_saved_close_quote = close_quote;
217   open_quote = "`";
218   close_quote = "'";
219 }
220 
221 /* Restore old values of open_quote and close_quote.  */
222 
~auto_fix_quotes()223 auto_fix_quotes::~auto_fix_quotes ()
224 {
225   open_quote = m_saved_open_quote;
226   close_quote = m_saved_close_quote;
227 }
228 
229 /* Read the contents of PATH into memory, returning a 0-terminated buffer
230    that must be freed by the caller.
231    Fail (and abort) if there are any problems, with LOC as the reported
232    location of the failure.  */
233 
234 char *
read_file(const location & loc,const char * path)235 read_file (const location &loc, const char *path)
236 {
237   FILE *f_in = fopen (path, "r");
238   if (!f_in)
239     fail_formatted (loc, "unable to open file: %s", path);
240 
241   /* Read content, allocating FIXME.  */
242   char *result = NULL;
243   size_t total_sz = 0;
244   size_t alloc_sz = 0;
245   char buf[4096];
246   size_t iter_sz_in;
247 
248   while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
249     {
250       gcc_assert (alloc_sz >= total_sz);
251       size_t old_total_sz = total_sz;
252       total_sz += iter_sz_in;
253       /* Allow 1 extra byte for 0-termination.  */
254       if (alloc_sz < (total_sz + 1))
255 	{
256 	  size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1;
257 	  result = (char *)xrealloc (result, new_alloc_sz);
258 	  alloc_sz = new_alloc_sz;
259 	}
260       memcpy (result + old_total_sz, buf, iter_sz_in);
261     }
262 
263   if (!feof (f_in))
264     fail_formatted (loc, "error reading from %s: %s", path,
265 		    xstrerror (errno));
266 
267   fclose (f_in);
268 
269   /* 0-terminate the buffer.  */
270   gcc_assert (total_sz < alloc_sz);
271   result[total_sz] = '\0';
272 
273   return result;
274 }
275 
276 /* The path of SRCDIR/testsuite/selftests.  */
277 
278 const char *path_to_selftest_files = NULL;
279 
280 /* Convert a path relative to SRCDIR/testsuite/selftests
281    to a real path (either absolute, or relative to pwd).
282    The result should be freed by the caller.  */
283 
284 char *
locate_file(const char * name)285 locate_file (const char *name)
286 {
287   ASSERT_NE (NULL, path_to_selftest_files);
288   return concat (path_to_selftest_files, "/", name, NULL);
289 }
290 
291 /* selftest::test_runner's ctor.  */
292 
test_runner(const char * name)293 test_runner::test_runner (const char *name)
294 : m_name (name),
295   m_start_time (get_run_time ())
296 {
297 }
298 
299 /* selftest::test_runner's dtor.  Print a summary line to stderr.  */
300 
~test_runner()301 test_runner::~test_runner ()
302 {
303   /* Finished running tests.  */
304   long finish_time = get_run_time ();
305   long elapsed_time = finish_time - m_start_time;
306 
307   fprintf (stderr,
308 	   "%s: %i pass(es) in %ld.%06ld seconds\n",
309 	   m_name, num_passes,
310 	   elapsed_time / 1000000, elapsed_time % 1000000);
311 }
312 
313 /* Selftests for libiberty.  */
314 
315 /* Verify that xstrndup generates EXPECTED when called on SRC and N.  */
316 
317 static void
assert_xstrndup_eq(const char * expected,const char * src,size_t n)318 assert_xstrndup_eq (const char *expected, const char *src, size_t n)
319 {
320   char *buf = xstrndup (src, n);
321   ASSERT_STREQ (expected, buf);
322   free (buf);
323 }
324 
325 /* Verify that xstrndup works as expected.  */
326 
327 static void
test_xstrndup()328 test_xstrndup ()
329 {
330   assert_xstrndup_eq ("", "test", 0);
331   assert_xstrndup_eq ("t", "test", 1);
332   assert_xstrndup_eq ("te", "test", 2);
333   assert_xstrndup_eq ("tes", "test", 3);
334   assert_xstrndup_eq ("test", "test", 4);
335   assert_xstrndup_eq ("test", "test", 5);
336 
337   /* Test on an string without zero termination.  */
338   const char src[4] = {'t', 'e', 's', 't'};
339   assert_xstrndup_eq ("", src, 0);
340   assert_xstrndup_eq ("t", src, 1);
341   assert_xstrndup_eq ("te", src, 2);
342   assert_xstrndup_eq ("tes", src, 3);
343   assert_xstrndup_eq ("test", src, 4);
344 }
345 
346 /* Run selftests for libiberty.  */
347 
348 static void
test_libiberty()349 test_libiberty ()
350 {
351   test_xstrndup ();
352 }
353 
354 /* Selftests for the selftest system itself.  */
355 
356 /* Sanity-check the ASSERT_ macros with various passing cases.  */
357 
358 static void
test_assertions()359 test_assertions ()
360 {
361   ASSERT_TRUE (true);
362   ASSERT_FALSE (false);
363   ASSERT_EQ (1, 1);
364   ASSERT_EQ_AT (SELFTEST_LOCATION, 1, 1);
365   ASSERT_NE (1, 2);
366   ASSERT_GT (2, 1);
367   ASSERT_GT_AT (SELFTEST_LOCATION, 2, 1);
368   ASSERT_LT (1, 2);
369   ASSERT_LT_AT (SELFTEST_LOCATION, 1, 2);
370   ASSERT_STREQ ("test", "test");
371   ASSERT_STREQ_AT (SELFTEST_LOCATION, "test", "test");
372   ASSERT_STR_CONTAINS ("foo bar baz", "bar");
373 }
374 
375 /* Verify named_temp_file.  */
376 
377 static void
test_named_temp_file()378 test_named_temp_file ()
379 {
380   named_temp_file t (".txt");
381   FILE *f = fopen (t.get_filename (), "w");
382   if (!f)
383     fail_formatted (SELFTEST_LOCATION,
384 		    "unable to open %s for writing", t.get_filename ());
385   fclose (f);
386 }
387 
388 /* Verify read_file (and also temp_source_file).  */
389 
390 static void
test_read_file()391 test_read_file ()
392 {
393   temp_source_file t (SELFTEST_LOCATION, "test1.s",
394 		      "\tjmp\t.L2\n");
395   char *buf = read_file (SELFTEST_LOCATION, t.get_filename ());
396   ASSERT_STREQ ("\tjmp\t.L2\n", buf);
397   free (buf);
398 }
399 
400 /* Verify locate_file (and read_file).  */
401 
402 static void
test_locate_file()403 test_locate_file ()
404 {
405   char *path = locate_file ("example.txt");
406   char *buf = read_file (SELFTEST_LOCATION, path);
407   ASSERT_STREQ ("example of a selftest file\n", buf);
408   free (buf);
409   free (path);
410 }
411 
412 /* Run all of the selftests within this file.  */
413 
414 void
selftest_cc_tests()415 selftest_cc_tests ()
416 {
417   test_libiberty ();
418   test_assertions ();
419   test_named_temp_file ();
420   test_read_file ();
421   test_locate_file ();
422 }
423 
424 } // namespace selftest
425 
426 #endif /* #if CHECKING_P */
427