[PATCH] RISC-V: Move UNSPEC_SSP_SET and UNSPEC_SSP_TEST to correct enum
[gcc.git] / gcc / selftest.cc
blob4fdce07c77f8c36d59496efc818f7e3d994bef6c
1 /* A self-testing framework, for use by -fself-test.
2 Copyright (C) 2015-2025 Free Software Foundation, Inc.
4 This file is part of GCC.
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.
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.
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/>. */
20 #include "config.h"
21 #include "system.h"
22 #include "coretypes.h"
23 #include "selftest.h"
24 #include "intl.h"
26 #if CHECKING_P
28 namespace selftest {
30 int num_passes;
32 /* Record the successful outcome of some aspect of a test. */
34 void
35 pass (const location &/*loc*/, const char */*msg*/)
37 num_passes++;
40 /* Report the failed outcome of some aspect of a test and abort. */
42 void
43 fail (const location &loc, const char *msg)
45 fprintf (stderr,"%s:%i: %s: FAIL: %s\n", loc.m_file, loc.m_line,
46 loc.m_function, msg);
47 abort ();
50 /* As "fail", but using printf-style formatted output. */
52 void
53 fail_formatted (const location &loc, const char *fmt, ...)
55 va_list ap;
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 ();
66 /* Invoke "diff" to print the difference between VAL1 and VAL2
67 on stdout. */
69 static void
70 print_diff (const location &loc, const char *val1, const char *val2)
72 temp_source_file tmpfile1 (loc, ".txt", val1);
73 temp_source_file tmpfile2 (loc, ".txt", val2);
74 const char *args[] = {"diff",
75 "-up",
76 tmpfile1.get_filename (),
77 tmpfile2.get_filename (),
78 NULL};
79 int exit_status = 0;
80 int err = 0;
81 pex_one (PEX_SEARCH | PEX_LAST,
82 args[0], CONST_CAST (char **, args),
83 NULL, NULL, NULL, &exit_status, &err);
86 /* Implementation detail of ASSERT_STREQ.
87 Compare val1 and val2 with strcmp. They ought
88 to be non-NULL; fail gracefully if either or both are NULL. */
90 void
91 assert_streq (const location &loc,
92 const char *desc_val1, const char *desc_val2,
93 const char *val1, const char *val2)
95 /* If val1 or val2 are NULL, fail with a custom error message. */
96 if (val1 == NULL)
97 if (val2 == NULL)
98 fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=NULL",
99 desc_val1, desc_val2);
100 else
101 fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=\"%s\"",
102 desc_val1, desc_val2, val2);
103 else
104 if (val2 == NULL)
105 fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=\"%s\" val2=NULL",
106 desc_val1, desc_val2, val1);
107 else
109 if (strcmp (val1, val2) == 0)
110 pass (loc, "ASSERT_STREQ");
111 else
113 print_diff (loc, val1, val2);
114 fail_formatted
115 (loc, "ASSERT_STREQ (%s, %s)\n val1=\"%s\"\n val2=\"%s\"\n",
116 desc_val1, desc_val2, val1, val2);
121 /* Implementation detail of ASSERT_STR_CONTAINS.
122 Use strstr to determine if val_needle is within val_haystack.
123 ::selftest::pass if it is found.
124 ::selftest::fail if it is not found. */
126 void
127 assert_str_contains (const location &loc,
128 const char *desc_haystack,
129 const char *desc_needle,
130 const char *val_haystack,
131 const char *val_needle)
133 /* If val_haystack is NULL, fail with a custom error message. */
134 if (val_haystack == NULL)
135 fail_formatted (loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=NULL",
136 desc_haystack, desc_needle);
138 /* If val_needle is NULL, fail with a custom error message. */
139 if (val_needle == NULL)
140 fail_formatted (loc,
141 "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=NULL",
142 desc_haystack, desc_needle, val_haystack);
144 const char *test = strstr (val_haystack, val_needle);
145 if (test)
146 pass (loc, "ASSERT_STR_CONTAINS");
147 else
148 fail_formatted
149 (loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=\"%s\"",
150 desc_haystack, desc_needle, val_haystack, val_needle);
153 /* Implementation detail of ASSERT_STR_STARTSWITH.
154 Determine if VAL_STR starts with VAL_PREFIX.
155 ::selftest::pass if VAL_STR does start with VAL_PREFIX.
156 ::selftest::fail if it does not, or either is NULL (using
157 DESC_STR and DESC_PREFIX in the error message). */
159 void
160 assert_str_startswith (const location &loc,
161 const char *desc_str,
162 const char *desc_prefix,
163 const char *val_str,
164 const char *val_prefix)
166 /* If val_str is NULL, fail with a custom error message. */
167 if (val_str == NULL)
168 fail_formatted (loc, "ASSERT_STR_STARTSWITH (%s, %s) str=NULL",
169 desc_str, desc_prefix);
171 /* If val_prefix is NULL, fail with a custom error message. */
172 if (val_prefix == NULL)
173 fail_formatted (loc,
174 "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=NULL",
175 desc_str, desc_prefix, val_str);
177 if (startswith (val_str, val_prefix))
178 pass (loc, "ASSERT_STR_STARTSWITH");
179 else
180 fail_formatted
181 (loc, "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=\"%s\"",
182 desc_str, desc_prefix, val_str, val_prefix);
186 /* Constructor. Generate a name for the file. */
188 named_temp_file::named_temp_file (const char *suffix,
189 file_cache *fc)
191 m_filename = make_temp_file (suffix);
192 ASSERT_NE (m_filename, NULL);
193 m_file_cache = fc;
196 /* Destructor. Delete the tempfile. */
198 named_temp_file::~named_temp_file ()
200 unlink (m_filename);
201 if (m_file_cache)
202 m_file_cache->forcibly_evict_file (m_filename);
203 free (m_filename);
206 /* Constructor. Create a tempfile using SUFFIX, and write CONTENT to
207 it. Abort if anything goes wrong, using LOC as the effective
208 location in the problem report. */
210 temp_source_file::temp_source_file (const location &loc,
211 const char *suffix,
212 const char *content,
213 file_cache *fc)
214 : named_temp_file (suffix, fc)
216 FILE *out = fopen (get_filename (), "w");
217 if (!out)
218 fail_formatted (loc, "unable to open tempfile: %s", get_filename ());
219 fprintf (out, "%s", content);
220 fclose (out);
223 /* As above, but with a size, to allow for NUL bytes in CONTENT. */
225 temp_source_file::temp_source_file (const location &loc,
226 const char *suffix,
227 const char *content,
228 size_t sz)
229 : named_temp_file (suffix)
231 FILE *out = fopen (get_filename (), "w");
232 if (!out)
233 fail_formatted (loc, "unable to open tempfile: %s", get_filename ());
234 fwrite (content, sz, 1, out);
235 fclose (out);
238 /* Avoid introducing locale-specific differences in the results
239 by hardcoding open_quote and close_quote. */
241 auto_fix_quotes::auto_fix_quotes ()
243 m_saved_open_quote = open_quote;
244 m_saved_close_quote = close_quote;
245 open_quote = "`";
246 close_quote = "'";
249 /* Restore old values of open_quote and close_quote. */
251 auto_fix_quotes::~auto_fix_quotes ()
253 open_quote = m_saved_open_quote;
254 close_quote = m_saved_close_quote;
257 /* Read the contents of PATH into memory, returning a 0-terminated buffer
258 that must be freed by the caller.
259 Fail (and abort) if there are any problems, with LOC as the reported
260 location of the failure. */
262 char *
263 read_file (const location &loc, const char *path)
265 FILE *f_in = fopen (path, "r");
266 if (!f_in)
267 fail_formatted (loc, "unable to open file: %s", path);
269 /* Read content, allocating FIXME. */
270 char *result = NULL;
271 size_t total_sz = 0;
272 size_t alloc_sz = 0;
273 char buf[4096];
274 size_t iter_sz_in;
276 while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
278 gcc_assert (alloc_sz >= total_sz);
279 size_t old_total_sz = total_sz;
280 total_sz += iter_sz_in;
281 /* Allow 1 extra byte for 0-termination. */
282 if (alloc_sz < (total_sz + 1))
284 size_t new_alloc_sz = alloc_sz ? alloc_sz * 2 : total_sz + 1;
285 result = (char *)xrealloc (result, new_alloc_sz);
286 alloc_sz = new_alloc_sz;
288 memcpy (result + old_total_sz, buf, iter_sz_in);
291 if (!feof (f_in))
292 fail_formatted (loc, "error reading from %s: %s", path,
293 xstrerror (errno));
295 fclose (f_in);
297 /* 0-terminate the buffer. */
298 gcc_assert (total_sz < alloc_sz);
299 result[total_sz] = '\0';
301 return result;
304 /* The path of SRCDIR/testsuite/selftests. */
306 const char *path_to_selftest_files = NULL;
308 /* Convert a path relative to SRCDIR/testsuite/selftests
309 to a real path (either absolute, or relative to pwd).
310 The result should be freed by the caller. */
312 char *
313 locate_file (const char *name)
315 ASSERT_NE (NULL, path_to_selftest_files);
316 return concat (path_to_selftest_files, "/", name, NULL);
319 /* selftest::test_runner's ctor. */
321 test_runner::test_runner (const char *name)
322 : m_name (name),
323 m_start_time (get_run_time ())
327 /* selftest::test_runner's dtor. Print a summary line to stderr. */
329 test_runner::~test_runner ()
331 /* Finished running tests. */
332 long finish_time = get_run_time ();
333 long elapsed_time = finish_time - m_start_time;
335 fprintf (stderr,
336 "%s: %i pass(es) in %ld.%06ld seconds\n",
337 m_name, num_passes,
338 elapsed_time / 1000000, elapsed_time % 1000000);
341 /* Selftests for libiberty. */
343 /* Verify that xstrndup generates EXPECTED when called on SRC and N. */
345 static void
346 assert_xstrndup_eq (const char *expected, const char *src, size_t n)
348 char *buf = xstrndup (src, n);
349 ASSERT_STREQ (expected, buf);
350 free (buf);
353 /* Verify that xstrndup works as expected. */
355 static void
356 test_xstrndup ()
358 assert_xstrndup_eq ("", "test", 0);
359 assert_xstrndup_eq ("t", "test", 1);
360 assert_xstrndup_eq ("te", "test", 2);
361 assert_xstrndup_eq ("tes", "test", 3);
362 assert_xstrndup_eq ("test", "test", 4);
363 assert_xstrndup_eq ("test", "test", 5);
365 /* Test on an string without zero termination. */
366 const char src[4] = {'t', 'e', 's', 't'};
367 assert_xstrndup_eq ("", src, 0);
368 assert_xstrndup_eq ("t", src, 1);
369 assert_xstrndup_eq ("te", src, 2);
370 assert_xstrndup_eq ("tes", src, 3);
371 assert_xstrndup_eq ("test", src, 4);
374 /* Run selftests for libiberty. */
376 static void
377 test_libiberty ()
379 test_xstrndup ();
382 /* Selftests for the selftest system itself. */
384 /* Sanity-check the ASSERT_ macros with various passing cases. */
386 static void
387 test_assertions ()
389 ASSERT_TRUE (true);
390 ASSERT_FALSE (false);
391 ASSERT_EQ (1, 1);
392 ASSERT_EQ_AT (SELFTEST_LOCATION, 1, 1);
393 ASSERT_NE (1, 2);
394 ASSERT_GT (2, 1);
395 ASSERT_GT_AT (SELFTEST_LOCATION, 2, 1);
396 ASSERT_LT (1, 2);
397 ASSERT_LT_AT (SELFTEST_LOCATION, 1, 2);
398 ASSERT_STREQ ("test", "test");
399 ASSERT_STREQ_AT (SELFTEST_LOCATION, "test", "test");
400 ASSERT_STR_CONTAINS ("foo bar baz", "bar");
403 /* Verify named_temp_file. */
405 static void
406 test_named_temp_file ()
408 named_temp_file t (".txt");
409 FILE *f = fopen (t.get_filename (), "w");
410 if (!f)
411 fail_formatted (SELFTEST_LOCATION,
412 "unable to open %s for writing", t.get_filename ());
413 fclose (f);
416 /* Verify read_file (and also temp_source_file). */
418 static void
419 test_read_file ()
421 temp_source_file t (SELFTEST_LOCATION, "test1.s",
422 "\tjmp\t.L2\n");
423 char *buf = read_file (SELFTEST_LOCATION, t.get_filename ());
424 ASSERT_STREQ ("\tjmp\t.L2\n", buf);
425 free (buf);
428 /* Verify locate_file (and read_file). */
430 static void
431 test_locate_file ()
433 char *path = locate_file ("example.txt");
434 char *buf = read_file (SELFTEST_LOCATION, path);
435 ASSERT_STREQ ("example of a selftest file\n", buf);
436 free (buf);
437 free (path);
440 /* Run all of the selftests within this file. */
442 void
443 selftest_cc_tests ()
445 test_libiberty ();
446 test_assertions ();
447 test_named_temp_file ();
448 test_read_file ();
449 test_locate_file ();
452 } // namespace selftest
454 #endif /* #if CHECKING_P */