Branch data Line data Source code
1 : : /* A self-testing framework, for use by -fself-test.
2 : : Copyright (C) 2015-2024 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
35 : 30580806 : pass (const location &/*loc*/, const char */*msg*/)
36 : : {
37 : 30580806 : num_passes++;
38 : 30580806 : }
39 : :
40 : : /* Report the failed outcome of some aspect of a test and abort. */
41 : :
42 : : void
43 : 0 : fail (const location &loc, const char *msg)
44 : : {
45 : 0 : fprintf (stderr,"%s:%i: %s: FAIL: %s\n", loc.m_file, loc.m_line,
46 : 0 : loc.m_function, msg);
47 : 0 : abort ();
48 : : }
49 : :
50 : : /* As "fail", but using printf-style formatted output. */
51 : :
52 : : void
53 : 0 : fail_formatted (const location &loc, const char *fmt, ...)
54 : : {
55 : 0 : va_list ap;
56 : :
57 : 0 : fprintf (stderr, "%s:%i: %s: FAIL: ", loc.m_file, loc.m_line,
58 : 0 : loc.m_function);
59 : 0 : va_start (ap, fmt);
60 : 0 : vfprintf (stderr, fmt, ap);
61 : 0 : va_end (ap);
62 : 0 : fprintf (stderr, "\n");
63 : 0 : abort ();
64 : : }
65 : :
66 : : /* Invoke "diff" to print the difference between VAL1 and VAL2
67 : : on stdout. */
68 : :
69 : : static void
70 : 0 : print_diff (const location &loc, const char *val1, const char *val2)
71 : : {
72 : 0 : temp_source_file tmpfile1 (loc, ".txt", val1);
73 : 0 : temp_source_file tmpfile2 (loc, ".txt", val2);
74 : 0 : const char *args[] = {"diff",
75 : : "-up",
76 : 0 : tmpfile1.get_filename (),
77 : 0 : tmpfile2.get_filename (),
78 : 0 : NULL};
79 : 0 : int exit_status = 0;
80 : 0 : int err = 0;
81 : 0 : pex_one (PEX_SEARCH | PEX_LAST,
82 : : args[0], CONST_CAST (char **, args),
83 : : NULL, NULL, NULL, &exit_status, &err);
84 : 0 : }
85 : :
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. */
89 : :
90 : : void
91 : 28894 : assert_streq (const location &loc,
92 : : const char *desc_val1, const char *desc_val2,
93 : : const char *val1, const char *val2)
94 : : {
95 : : /* If val1 or val2 are NULL, fail with a custom error message. */
96 : 28894 : if (val1 == NULL)
97 : 0 : if (val2 == NULL)
98 : 0 : fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=NULL",
99 : : desc_val1, desc_val2);
100 : : else
101 : 0 : fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=\"%s\"",
102 : : desc_val1, desc_val2, val2);
103 : : else
104 : 28894 : if (val2 == NULL)
105 : 0 : fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=\"%s\" val2=NULL",
106 : : desc_val1, desc_val2, val1);
107 : : else
108 : : {
109 : 28894 : if (strcmp (val1, val2) == 0)
110 : 28894 : pass (loc, "ASSERT_STREQ");
111 : : else
112 : : {
113 : 0 : print_diff (loc, val1, val2);
114 : 0 : fail_formatted
115 : 0 : (loc, "ASSERT_STREQ (%s, %s)\n val1=\"%s\"\n val2=\"%s\"\n",
116 : : desc_val1, desc_val2, val1, val2);
117 : : }
118 : : }
119 : 28894 : }
120 : :
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. */
125 : :
126 : : void
127 : 2112 : 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)
132 : : {
133 : : /* If val_haystack is NULL, fail with a custom error message. */
134 : 2112 : if (val_haystack == NULL)
135 : 0 : fail_formatted (loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=NULL",
136 : : desc_haystack, desc_needle);
137 : :
138 : : /* If val_needle is NULL, fail with a custom error message. */
139 : 2112 : if (val_needle == NULL)
140 : 0 : fail_formatted (loc,
141 : : "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=NULL",
142 : : desc_haystack, desc_needle, val_haystack);
143 : :
144 : 2112 : const char *test = strstr (val_haystack, val_needle);
145 : 2112 : if (test)
146 : 2112 : pass (loc, "ASSERT_STR_CONTAINS");
147 : : else
148 : 0 : fail_formatted
149 : 0 : (loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=\"%s\"",
150 : : desc_haystack, desc_needle, val_haystack, val_needle);
151 : 2112 : }
152 : :
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). */
158 : :
159 : : void
160 : 332 : 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)
165 : : {
166 : : /* If val_str is NULL, fail with a custom error message. */
167 : 332 : if (val_str == NULL)
168 : 0 : fail_formatted (loc, "ASSERT_STR_STARTSWITH (%s, %s) str=NULL",
169 : : desc_str, desc_prefix);
170 : :
171 : : /* If val_prefix is NULL, fail with a custom error message. */
172 : 332 : if (val_prefix == NULL)
173 : 0 : fail_formatted (loc,
174 : : "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=NULL",
175 : : desc_str, desc_prefix, val_str);
176 : :
177 : 332 : if (startswith (val_str, val_prefix))
178 : 332 : pass (loc, "ASSERT_STR_STARTSWITH");
179 : : else
180 : 0 : fail_formatted
181 : 0 : (loc, "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=\"%s\"",
182 : : desc_str, desc_prefix, val_str, val_prefix);
183 : 332 : }
184 : :
185 : :
186 : : /* Constructor. Generate a name for the file. */
187 : :
188 : 6508 : named_temp_file::named_temp_file (const char *suffix,
189 : 6508 : file_cache *fc)
190 : : {
191 : 6508 : m_filename = make_temp_file (suffix);
192 : 6508 : ASSERT_NE (m_filename, NULL);
193 : 6508 : m_file_cache = fc;
194 : 6508 : }
195 : :
196 : : /* Destructor. Delete the tempfile. */
197 : :
198 : 6508 : named_temp_file::~named_temp_file ()
199 : : {
200 : 6508 : unlink (m_filename);
201 : 6508 : if (m_file_cache)
202 : 576 : m_file_cache->forcibly_evict_file (m_filename);
203 : 6508 : free (m_filename);
204 : 6508 : }
205 : :
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. */
209 : :
210 : 5992 : temp_source_file::temp_source_file (const location &loc,
211 : : const char *suffix,
212 : : const char *content,
213 : 5992 : file_cache *fc)
214 : 5992 : : named_temp_file (suffix, fc)
215 : : {
216 : 5992 : FILE *out = fopen (get_filename (), "w");
217 : 5992 : if (!out)
218 : 0 : fail_formatted (loc, "unable to open tempfile: %s", get_filename ());
219 : 5992 : fprintf (out, "%s", content);
220 : 5992 : fclose (out);
221 : 5992 : }
222 : :
223 : : /* As above, but with a size, to allow for NUL bytes in CONTENT. */
224 : :
225 : 192 : temp_source_file::temp_source_file (const location &loc,
226 : : const char *suffix,
227 : : const char *content,
228 : 192 : size_t sz)
229 : 192 : : named_temp_file (suffix)
230 : : {
231 : 192 : FILE *out = fopen (get_filename (), "w");
232 : 192 : if (!out)
233 : 0 : fail_formatted (loc, "unable to open tempfile: %s", get_filename ());
234 : 192 : fwrite (content, sz, 1, out);
235 : 192 : fclose (out);
236 : 192 : }
237 : :
238 : : /* Avoid introducing locale-specific differences in the results
239 : : by hardcoding open_quote and close_quote. */
240 : :
241 : 640 : auto_fix_quotes::auto_fix_quotes ()
242 : : {
243 : 640 : m_saved_open_quote = open_quote;
244 : 640 : m_saved_close_quote = close_quote;
245 : 640 : open_quote = "`";
246 : 640 : close_quote = "'";
247 : 640 : }
248 : :
249 : : /* Restore old values of open_quote and close_quote. */
250 : :
251 : 640 : auto_fix_quotes::~auto_fix_quotes ()
252 : : {
253 : 640 : open_quote = m_saved_open_quote;
254 : 640 : close_quote = m_saved_close_quote;
255 : 640 : }
256 : :
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. */
261 : :
262 : : char *
263 : 112 : read_file (const location &loc, const char *path)
264 : : {
265 : 112 : FILE *f_in = fopen (path, "r");
266 : 112 : if (!f_in)
267 : 0 : fail_formatted (loc, "unable to open file: %s", path);
268 : :
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;
275 : :
276 : 224 : while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
277 : : {
278 : 112 : gcc_assert (alloc_sz >= total_sz);
279 : 112 : size_t old_total_sz = total_sz;
280 : 112 : total_sz += iter_sz_in;
281 : : /* Allow 1 extra byte for 0-termination. */
282 : 112 : if (alloc_sz < (total_sz + 1))
283 : : {
284 : 112 : size_t new_alloc_sz = alloc_sz ? alloc_sz * 2 : total_sz + 1;
285 : 112 : result = (char *)xrealloc (result, new_alloc_sz);
286 : 112 : alloc_sz = new_alloc_sz;
287 : : }
288 : 112 : memcpy (result + old_total_sz, buf, iter_sz_in);
289 : : }
290 : :
291 : 112 : if (!feof (f_in))
292 : 0 : fail_formatted (loc, "error reading from %s: %s", path,
293 : 0 : xstrerror (errno));
294 : :
295 : 112 : fclose (f_in);
296 : :
297 : : /* 0-terminate the buffer. */
298 : 112 : gcc_assert (total_sz < alloc_sz);
299 : 112 : result[total_sz] = '\0';
300 : :
301 : 112 : return result;
302 : : }
303 : :
304 : : /* The path of SRCDIR/testsuite/selftests. */
305 : :
306 : : const char *path_to_selftest_files = NULL;
307 : :
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. */
311 : :
312 : : char *
313 : 80 : locate_file (const char *name)
314 : : {
315 : 80 : ASSERT_NE (NULL, path_to_selftest_files);
316 : 80 : return concat (path_to_selftest_files, "/", name, NULL);
317 : : }
318 : :
319 : : /* selftest::test_runner's ctor. */
320 : :
321 : 5 : test_runner::test_runner (const char *name)
322 : 5 : : m_name (name),
323 : 5 : m_start_time (get_run_time ())
324 : : {
325 : 5 : }
326 : :
327 : : /* selftest::test_runner's dtor. Print a summary line to stderr. */
328 : :
329 : 5 : test_runner::~test_runner ()
330 : : {
331 : : /* Finished running tests. */
332 : 5 : long finish_time = get_run_time ();
333 : 5 : long elapsed_time = finish_time - m_start_time;
334 : :
335 : 5 : fprintf (stderr,
336 : : "%s: %i pass(es) in %ld.%06ld seconds\n",
337 : : m_name, num_passes,
338 : : elapsed_time / 1000000, elapsed_time % 1000000);
339 : 5 : }
340 : :
341 : : /* Selftests for libiberty. */
342 : :
343 : : /* Verify that xstrndup generates EXPECTED when called on SRC and N. */
344 : :
345 : : static void
346 : 44 : assert_xstrndup_eq (const char *expected, const char *src, size_t n)
347 : : {
348 : 44 : char *buf = xstrndup (src, n);
349 : 44 : ASSERT_STREQ (expected, buf);
350 : 44 : free (buf);
351 : 44 : }
352 : :
353 : : /* Verify that xstrndup works as expected. */
354 : :
355 : : static void
356 : 4 : test_xstrndup ()
357 : : {
358 : 4 : assert_xstrndup_eq ("", "test", 0);
359 : 4 : assert_xstrndup_eq ("t", "test", 1);
360 : 4 : assert_xstrndup_eq ("te", "test", 2);
361 : 4 : assert_xstrndup_eq ("tes", "test", 3);
362 : 4 : assert_xstrndup_eq ("test", "test", 4);
363 : 4 : assert_xstrndup_eq ("test", "test", 5);
364 : :
365 : : /* Test on an string without zero termination. */
366 : 4 : const char src[4] = {'t', 'e', 's', 't'};
367 : 4 : assert_xstrndup_eq ("", src, 0);
368 : 4 : assert_xstrndup_eq ("t", src, 1);
369 : 4 : assert_xstrndup_eq ("te", src, 2);
370 : 4 : assert_xstrndup_eq ("tes", src, 3);
371 : 4 : assert_xstrndup_eq ("test", src, 4);
372 : 4 : }
373 : :
374 : : /* Run selftests for libiberty. */
375 : :
376 : : static void
377 : 4 : test_libiberty ()
378 : : {
379 : 0 : test_xstrndup ();
380 : 0 : }
381 : :
382 : : /* Selftests for the selftest system itself. */
383 : :
384 : : /* Sanity-check the ASSERT_ macros with various passing cases. */
385 : :
386 : : static void
387 : 4 : test_assertions ()
388 : : {
389 : 4 : ASSERT_TRUE (true);
390 : 4 : ASSERT_FALSE (false);
391 : 4 : ASSERT_EQ (1, 1);
392 : 4 : ASSERT_EQ_AT (SELFTEST_LOCATION, 1, 1);
393 : 4 : ASSERT_NE (1, 2);
394 : 4 : ASSERT_GT (2, 1);
395 : 4 : ASSERT_GT_AT (SELFTEST_LOCATION, 2, 1);
396 : 4 : ASSERT_LT (1, 2);
397 : 4 : ASSERT_LT_AT (SELFTEST_LOCATION, 1, 2);
398 : 4 : ASSERT_STREQ ("test", "test");
399 : 4 : ASSERT_STREQ_AT (SELFTEST_LOCATION, "test", "test");
400 : 4 : ASSERT_STR_CONTAINS ("foo bar baz", "bar");
401 : 4 : }
402 : :
403 : : /* Verify named_temp_file. */
404 : :
405 : : static void
406 : 4 : test_named_temp_file ()
407 : : {
408 : 4 : named_temp_file t (".txt");
409 : 4 : FILE *f = fopen (t.get_filename (), "w");
410 : 4 : if (!f)
411 : 0 : fail_formatted (SELFTEST_LOCATION,
412 : : "unable to open %s for writing", t.get_filename ());
413 : 4 : fclose (f);
414 : 4 : }
415 : :
416 : : /* Verify read_file (and also temp_source_file). */
417 : :
418 : : static void
419 : 4 : test_read_file ()
420 : : {
421 : 4 : temp_source_file t (SELFTEST_LOCATION, "test1.s",
422 : 4 : "\tjmp\t.L2\n");
423 : 4 : char *buf = read_file (SELFTEST_LOCATION, t.get_filename ());
424 : 4 : ASSERT_STREQ ("\tjmp\t.L2\n", buf);
425 : 4 : free (buf);
426 : 4 : }
427 : :
428 : : /* Verify locate_file (and read_file). */
429 : :
430 : : static void
431 : 4 : test_locate_file ()
432 : : {
433 : 4 : char *path = locate_file ("example.txt");
434 : 4 : char *buf = read_file (SELFTEST_LOCATION, path);
435 : 4 : ASSERT_STREQ ("example of a selftest file\n", buf);
436 : 4 : free (buf);
437 : 4 : free (path);
438 : 4 : }
439 : :
440 : : /* Run all of the selftests within this file. */
441 : :
442 : : void
443 : 4 : selftest_cc_tests ()
444 : : {
445 : 4 : test_libiberty ();
446 : 4 : test_assertions ();
447 : 4 : test_named_temp_file ();
448 : 4 : test_read_file ();
449 : 4 : test_locate_file ();
450 : 4 : }
451 : :
452 : : } // namespace selftest
453 : :
454 : : #endif /* #if CHECKING_P */
|