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 : 30564176 : pass (const location &/*loc*/, const char */*msg*/)
36 : : {
37 : 30564176 : num_passes++;
38 : 30564176 : }
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 : : /* 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
71 : 26052 : 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 : 26052 : if (val1 == NULL)
77 : 0 : if (val2 == NULL)
78 : 0 : fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=NULL",
79 : : desc_val1, desc_val2);
80 : : else
81 : 0 : fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=\"%s\"",
82 : : desc_val1, desc_val2, val2);
83 : : else
84 : 26052 : if (val2 == NULL)
85 : 0 : fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=\"%s\" val2=NULL",
86 : : desc_val1, desc_val2, val1);
87 : : else
88 : : {
89 : 26052 : if (strcmp (val1, val2) == 0)
90 : 26052 : pass (loc, "ASSERT_STREQ");
91 : : else
92 : 0 : fail_formatted (loc, "ASSERT_STREQ (%s, %s)\n val1=\"%s\"\n val2=\"%s\"\n",
93 : : desc_val1, desc_val2, val1, val2);
94 : : }
95 : 26052 : }
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
103 : 2112 : 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 : 2112 : if (val_haystack == NULL)
111 : 0 : 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 : 2112 : if (val_needle == NULL)
116 : 0 : fail_formatted (loc,
117 : : "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=NULL",
118 : : desc_haystack, desc_needle, val_haystack);
119 : :
120 : 2112 : const char *test = strstr (val_haystack, val_needle);
121 : 2112 : if (test)
122 : 2112 : pass (loc, "ASSERT_STR_CONTAINS");
123 : : else
124 : 0 : fail_formatted
125 : 0 : (loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=\"%s\"",
126 : : desc_haystack, desc_needle, val_haystack, val_needle);
127 : 2112 : }
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
136 : 332 : 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 : 332 : if (val_str == NULL)
144 : 0 : 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 : 332 : if (val_prefix == NULL)
149 : 0 : fail_formatted (loc,
150 : : "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=NULL",
151 : : desc_str, desc_prefix, val_str);
152 : :
153 : 332 : if (startswith (val_str, val_prefix))
154 : 332 : pass (loc, "ASSERT_STR_STARTSWITH");
155 : : else
156 : 0 : fail_formatted
157 : 0 : (loc, "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=\"%s\"",
158 : : desc_str, desc_prefix, val_str, val_prefix);
159 : 332 : }
160 : :
161 : :
162 : : /* Constructor. Generate a name for the file. */
163 : :
164 : 5668 : named_temp_file::named_temp_file (const char *suffix,
165 : 5668 : file_cache *fc)
166 : : {
167 : 5668 : m_filename = make_temp_file (suffix);
168 : 5668 : ASSERT_NE (m_filename, NULL);
169 : 5668 : m_file_cache = fc;
170 : 5668 : }
171 : :
172 : : /* Destructor. Delete the tempfile. */
173 : :
174 : 5668 : named_temp_file::~named_temp_file ()
175 : : {
176 : 5668 : unlink (m_filename);
177 : 5668 : if (m_file_cache)
178 : 96 : m_file_cache->forcibly_evict_file (m_filename);
179 : 5668 : free (m_filename);
180 : 5668 : }
181 : :
182 : : /* Constructor. Create a tempfile using SUFFIX, and write CONTENT to
183 : : it. Abort if anything goes wrong, using LOC as the effective
184 : : location in the problem report. */
185 : :
186 : 5032 : temp_source_file::temp_source_file (const location &loc,
187 : : const char *suffix,
188 : : const char *content,
189 : 5032 : file_cache *fc)
190 : 5032 : : named_temp_file (suffix, fc)
191 : : {
192 : 5032 : FILE *out = fopen (get_filename (), "w");
193 : 5032 : if (!out)
194 : 0 : fail_formatted (loc, "unable to open tempfile: %s", get_filename ());
195 : 5032 : fprintf (out, "%s", content);
196 : 5032 : fclose (out);
197 : 5032 : }
198 : :
199 : : /* As above, but with a size, to allow for NUL bytes in CONTENT. */
200 : :
201 : 192 : temp_source_file::temp_source_file (const location &loc,
202 : : const char *suffix,
203 : : const char *content,
204 : 192 : size_t sz)
205 : 192 : : named_temp_file (suffix)
206 : : {
207 : 192 : FILE *out = fopen (get_filename (), "w");
208 : 192 : if (!out)
209 : 0 : fail_formatted (loc, "unable to open tempfile: %s", get_filename ());
210 : 192 : fwrite (content, sz, 1, out);
211 : 192 : fclose (out);
212 : 192 : }
213 : :
214 : : /* Avoid introducing locale-specific differences in the results
215 : : by hardcoding open_quote and close_quote. */
216 : :
217 : 416 : auto_fix_quotes::auto_fix_quotes ()
218 : : {
219 : 416 : m_saved_open_quote = open_quote;
220 : 416 : m_saved_close_quote = close_quote;
221 : 416 : open_quote = "`";
222 : 416 : close_quote = "'";
223 : 416 : }
224 : :
225 : : /* Restore old values of open_quote and close_quote. */
226 : :
227 : 416 : auto_fix_quotes::~auto_fix_quotes ()
228 : : {
229 : 416 : open_quote = m_saved_open_quote;
230 : 416 : close_quote = m_saved_close_quote;
231 : 416 : }
232 : :
233 : : /* Read the contents of PATH into memory, returning a 0-terminated buffer
234 : : that must be freed by the caller.
235 : : Fail (and abort) if there are any problems, with LOC as the reported
236 : : location of the failure. */
237 : :
238 : : char *
239 : 112 : read_file (const location &loc, const char *path)
240 : : {
241 : 112 : FILE *f_in = fopen (path, "r");
242 : 112 : if (!f_in)
243 : 0 : fail_formatted (loc, "unable to open file: %s", path);
244 : :
245 : : /* Read content, allocating FIXME. */
246 : : char *result = NULL;
247 : : size_t total_sz = 0;
248 : : size_t alloc_sz = 0;
249 : : char buf[4096];
250 : : size_t iter_sz_in;
251 : :
252 : 224 : while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
253 : : {
254 : 112 : gcc_assert (alloc_sz >= total_sz);
255 : 112 : size_t old_total_sz = total_sz;
256 : 112 : total_sz += iter_sz_in;
257 : : /* Allow 1 extra byte for 0-termination. */
258 : 112 : if (alloc_sz < (total_sz + 1))
259 : : {
260 : 112 : size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1;
261 : 112 : result = (char *)xrealloc (result, new_alloc_sz);
262 : 112 : alloc_sz = new_alloc_sz;
263 : : }
264 : 112 : memcpy (result + old_total_sz, buf, iter_sz_in);
265 : : }
266 : :
267 : 112 : if (!feof (f_in))
268 : 0 : fail_formatted (loc, "error reading from %s: %s", path,
269 : 0 : xstrerror (errno));
270 : :
271 : 112 : fclose (f_in);
272 : :
273 : : /* 0-terminate the buffer. */
274 : 112 : gcc_assert (total_sz < alloc_sz);
275 : 112 : result[total_sz] = '\0';
276 : :
277 : 112 : return result;
278 : : }
279 : :
280 : : /* The path of SRCDIR/testsuite/selftests. */
281 : :
282 : : const char *path_to_selftest_files = NULL;
283 : :
284 : : /* Convert a path relative to SRCDIR/testsuite/selftests
285 : : to a real path (either absolute, or relative to pwd).
286 : : The result should be freed by the caller. */
287 : :
288 : : char *
289 : 80 : locate_file (const char *name)
290 : : {
291 : 80 : ASSERT_NE (NULL, path_to_selftest_files);
292 : 80 : return concat (path_to_selftest_files, "/", name, NULL);
293 : : }
294 : :
295 : : /* selftest::test_runner's ctor. */
296 : :
297 : 5 : test_runner::test_runner (const char *name)
298 : 5 : : m_name (name),
299 : 5 : m_start_time (get_run_time ())
300 : : {
301 : 5 : }
302 : :
303 : : /* selftest::test_runner's dtor. Print a summary line to stderr. */
304 : :
305 : 5 : test_runner::~test_runner ()
306 : : {
307 : : /* Finished running tests. */
308 : 5 : long finish_time = get_run_time ();
309 : 5 : long elapsed_time = finish_time - m_start_time;
310 : :
311 : 5 : fprintf (stderr,
312 : : "%s: %i pass(es) in %ld.%06ld seconds\n",
313 : : m_name, num_passes,
314 : : elapsed_time / 1000000, elapsed_time % 1000000);
315 : 5 : }
316 : :
317 : : /* Selftests for libiberty. */
318 : :
319 : : /* Verify that xstrndup generates EXPECTED when called on SRC and N. */
320 : :
321 : : static void
322 : 44 : assert_xstrndup_eq (const char *expected, const char *src, size_t n)
323 : : {
324 : 44 : char *buf = xstrndup (src, n);
325 : 44 : ASSERT_STREQ (expected, buf);
326 : 44 : free (buf);
327 : 44 : }
328 : :
329 : : /* Verify that xstrndup works as expected. */
330 : :
331 : : static void
332 : 4 : test_xstrndup ()
333 : : {
334 : 4 : assert_xstrndup_eq ("", "test", 0);
335 : 4 : assert_xstrndup_eq ("t", "test", 1);
336 : 4 : assert_xstrndup_eq ("te", "test", 2);
337 : 4 : assert_xstrndup_eq ("tes", "test", 3);
338 : 4 : assert_xstrndup_eq ("test", "test", 4);
339 : 4 : assert_xstrndup_eq ("test", "test", 5);
340 : :
341 : : /* Test on an string without zero termination. */
342 : 4 : const char src[4] = {'t', 'e', 's', 't'};
343 : 4 : assert_xstrndup_eq ("", src, 0);
344 : 4 : assert_xstrndup_eq ("t", src, 1);
345 : 4 : assert_xstrndup_eq ("te", src, 2);
346 : 4 : assert_xstrndup_eq ("tes", src, 3);
347 : 4 : assert_xstrndup_eq ("test", src, 4);
348 : 4 : }
349 : :
350 : : /* Run selftests for libiberty. */
351 : :
352 : : static void
353 : 4 : test_libiberty ()
354 : : {
355 : 0 : test_xstrndup ();
356 : 0 : }
357 : :
358 : : /* Selftests for the selftest system itself. */
359 : :
360 : : /* Sanity-check the ASSERT_ macros with various passing cases. */
361 : :
362 : : static void
363 : 4 : test_assertions ()
364 : : {
365 : 4 : ASSERT_TRUE (true);
366 : 4 : ASSERT_FALSE (false);
367 : 4 : ASSERT_EQ (1, 1);
368 : 4 : ASSERT_EQ_AT (SELFTEST_LOCATION, 1, 1);
369 : 4 : ASSERT_NE (1, 2);
370 : 4 : ASSERT_GT (2, 1);
371 : 4 : ASSERT_GT_AT (SELFTEST_LOCATION, 2, 1);
372 : 4 : ASSERT_LT (1, 2);
373 : 4 : ASSERT_LT_AT (SELFTEST_LOCATION, 1, 2);
374 : 4 : ASSERT_STREQ ("test", "test");
375 : 4 : ASSERT_STREQ_AT (SELFTEST_LOCATION, "test", "test");
376 : 4 : ASSERT_STR_CONTAINS ("foo bar baz", "bar");
377 : 4 : }
378 : :
379 : : /* Verify named_temp_file. */
380 : :
381 : : static void
382 : 4 : test_named_temp_file ()
383 : : {
384 : 4 : named_temp_file t (".txt");
385 : 4 : FILE *f = fopen (t.get_filename (), "w");
386 : 4 : if (!f)
387 : 0 : fail_formatted (SELFTEST_LOCATION,
388 : : "unable to open %s for writing", t.get_filename ());
389 : 4 : fclose (f);
390 : 4 : }
391 : :
392 : : /* Verify read_file (and also temp_source_file). */
393 : :
394 : : static void
395 : 4 : test_read_file ()
396 : : {
397 : 4 : temp_source_file t (SELFTEST_LOCATION, "test1.s",
398 : 4 : "\tjmp\t.L2\n");
399 : 4 : char *buf = read_file (SELFTEST_LOCATION, t.get_filename ());
400 : 4 : ASSERT_STREQ ("\tjmp\t.L2\n", buf);
401 : 4 : free (buf);
402 : 4 : }
403 : :
404 : : /* Verify locate_file (and read_file). */
405 : :
406 : : static void
407 : 4 : test_locate_file ()
408 : : {
409 : 4 : char *path = locate_file ("example.txt");
410 : 4 : char *buf = read_file (SELFTEST_LOCATION, path);
411 : 4 : ASSERT_STREQ ("example of a selftest file\n", buf);
412 : 4 : free (buf);
413 : 4 : free (path);
414 : 4 : }
415 : :
416 : : /* Run all of the selftests within this file. */
417 : :
418 : : void
419 : 4 : selftest_cc_tests ()
420 : : {
421 : 4 : test_libiberty ();
422 : 4 : test_assertions ();
423 : 4 : test_named_temp_file ();
424 : 4 : test_read_file ();
425 : 4 : test_locate_file ();
426 : 4 : }
427 : :
428 : : } // namespace selftest
429 : :
430 : : #endif /* #if CHECKING_P */
|