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