]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/selftest.c
Correct a function pre/postcondition [PR102403].
[thirdparty/gcc.git] / gcc / selftest.c
1 /* A self-testing framework, for use by -fself-test.
2 Copyright (C) 2015-2021 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 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
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
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
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) val1=\"%s\" val2=\"%s\"",
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
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
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
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
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
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 /* Avoid introducing locale-specific differences in the results
196 by hardcoding open_quote and close_quote. */
197
198 auto_fix_quotes::auto_fix_quotes ()
199 {
200 m_saved_open_quote = open_quote;
201 m_saved_close_quote = close_quote;
202 open_quote = "`";
203 close_quote = "'";
204 }
205
206 /* Restore old values of open_quote and close_quote. */
207
208 auto_fix_quotes::~auto_fix_quotes ()
209 {
210 open_quote = m_saved_open_quote;
211 close_quote = m_saved_close_quote;
212 }
213
214 /* Read the contents of PATH into memory, returning a 0-terminated buffer
215 that must be freed by the caller.
216 Fail (and abort) if there are any problems, with LOC as the reported
217 location of the failure. */
218
219 char *
220 read_file (const location &loc, const char *path)
221 {
222 FILE *f_in = fopen (path, "r");
223 if (!f_in)
224 fail_formatted (loc, "unable to open file: %s", path);
225
226 /* Read content, allocating FIXME. */
227 char *result = NULL;
228 size_t total_sz = 0;
229 size_t alloc_sz = 0;
230 char buf[4096];
231 size_t iter_sz_in;
232
233 while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
234 {
235 gcc_assert (alloc_sz >= total_sz);
236 size_t old_total_sz = total_sz;
237 total_sz += iter_sz_in;
238 /* Allow 1 extra byte for 0-termination. */
239 if (alloc_sz < (total_sz + 1))
240 {
241 size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1;
242 result = (char *)xrealloc (result, new_alloc_sz);
243 alloc_sz = new_alloc_sz;
244 }
245 memcpy (result + old_total_sz, buf, iter_sz_in);
246 }
247
248 if (!feof (f_in))
249 fail_formatted (loc, "error reading from %s: %s", path,
250 xstrerror (errno));
251
252 fclose (f_in);
253
254 /* 0-terminate the buffer. */
255 gcc_assert (total_sz < alloc_sz);
256 result[total_sz] = '\0';
257
258 return result;
259 }
260
261 /* The path of SRCDIR/testsuite/selftests. */
262
263 const char *path_to_selftest_files = NULL;
264
265 /* Convert a path relative to SRCDIR/testsuite/selftests
266 to a real path (either absolute, or relative to pwd).
267 The result should be freed by the caller. */
268
269 char *
270 locate_file (const char *name)
271 {
272 ASSERT_NE (NULL, path_to_selftest_files);
273 return concat (path_to_selftest_files, "/", name, NULL);
274 }
275
276 /* selftest::test_runner's ctor. */
277
278 test_runner::test_runner (const char *name)
279 : m_name (name),
280 m_start_time (get_run_time ())
281 {
282 }
283
284 /* selftest::test_runner's dtor. Print a summary line to stderr. */
285
286 test_runner::~test_runner ()
287 {
288 /* Finished running tests. */
289 long finish_time = get_run_time ();
290 long elapsed_time = finish_time - m_start_time;
291
292 fprintf (stderr,
293 "%s: %i pass(es) in %ld.%06ld seconds\n",
294 m_name, num_passes,
295 elapsed_time / 1000000, elapsed_time % 1000000);
296 }
297
298 /* Selftests for libiberty. */
299
300 /* Verify that xstrndup generates EXPECTED when called on SRC and N. */
301
302 static void
303 assert_xstrndup_eq (const char *expected, const char *src, size_t n)
304 {
305 char *buf = xstrndup (src, n);
306 ASSERT_STREQ (expected, buf);
307 free (buf);
308 }
309
310 /* Verify that xstrndup works as expected. */
311
312 static void
313 test_xstrndup ()
314 {
315 assert_xstrndup_eq ("", "test", 0);
316 assert_xstrndup_eq ("t", "test", 1);
317 assert_xstrndup_eq ("te", "test", 2);
318 assert_xstrndup_eq ("tes", "test", 3);
319 assert_xstrndup_eq ("test", "test", 4);
320 assert_xstrndup_eq ("test", "test", 5);
321
322 /* Test on an string without zero termination. */
323 const char src[4] = {'t', 'e', 's', 't'};
324 assert_xstrndup_eq ("", src, 0);
325 assert_xstrndup_eq ("t", src, 1);
326 assert_xstrndup_eq ("te", src, 2);
327 assert_xstrndup_eq ("tes", src, 3);
328 assert_xstrndup_eq ("test", src, 4);
329 }
330
331 /* Run selftests for libiberty. */
332
333 static void
334 test_libiberty ()
335 {
336 test_xstrndup ();
337 }
338
339 /* Selftests for the selftest system itself. */
340
341 /* Sanity-check the ASSERT_ macros with various passing cases. */
342
343 static void
344 test_assertions ()
345 {
346 ASSERT_TRUE (true);
347 ASSERT_FALSE (false);
348 ASSERT_EQ (1, 1);
349 ASSERT_EQ_AT (SELFTEST_LOCATION, 1, 1);
350 ASSERT_NE (1, 2);
351 ASSERT_GT (2, 1);
352 ASSERT_GT_AT (SELFTEST_LOCATION, 2, 1);
353 ASSERT_LT (1, 2);
354 ASSERT_LT_AT (SELFTEST_LOCATION, 1, 2);
355 ASSERT_STREQ ("test", "test");
356 ASSERT_STREQ_AT (SELFTEST_LOCATION, "test", "test");
357 ASSERT_STR_CONTAINS ("foo bar baz", "bar");
358 }
359
360 /* Verify named_temp_file. */
361
362 static void
363 test_named_temp_file ()
364 {
365 named_temp_file t (".txt");
366 FILE *f = fopen (t.get_filename (), "w");
367 if (!f)
368 fail_formatted (SELFTEST_LOCATION,
369 "unable to open %s for writing", t.get_filename ());
370 fclose (f);
371 }
372
373 /* Verify read_file (and also temp_source_file). */
374
375 static void
376 test_read_file ()
377 {
378 temp_source_file t (SELFTEST_LOCATION, "test1.s",
379 "\tjmp\t.L2\n");
380 char *buf = read_file (SELFTEST_LOCATION, t.get_filename ());
381 ASSERT_STREQ ("\tjmp\t.L2\n", buf);
382 free (buf);
383 }
384
385 /* Verify locate_file (and read_file). */
386
387 static void
388 test_locate_file ()
389 {
390 char *path = locate_file ("example.txt");
391 char *buf = read_file (SELFTEST_LOCATION, path);
392 ASSERT_STREQ ("example of a selftest file\n", buf);
393 free (buf);
394 free (path);
395 }
396
397 /* Run all of the selftests within this file. */
398
399 void
400 selftest_c_tests ()
401 {
402 test_libiberty ();
403 test_assertions ();
404 test_named_temp_file ();
405 test_read_file ();
406 test_locate_file ();
407 }
408
409 } // namespace selftest
410
411 #endif /* #if CHECKING_P */