1 /* A self-testing framework, for use by -fself-test. 2 Copyright (C) 2015-2018 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 25 #if CHECKING_P 26 27 namespace selftest { 28 29 int num_passes; 30 31 /* Record the successful outcome of some aspect of a test. */ 32 33 void 34 pass (const location &/*loc*/, const char */*msg*/) 35 { 36 num_passes++; 37 } 38 39 /* Report the failed outcome of some aspect of a test and abort. */ 40 41 void 42 fail (const location &loc, const char *msg) 43 { 44 fprintf (stderr,"%s:%i: %s: FAIL: %s\n", loc.m_file, loc.m_line, 45 loc.m_function, msg); 46 abort (); 47 } 48 49 /* As "fail", but using printf-style formatted output. */ 50 51 void 52 fail_formatted (const location &loc, const char *fmt, ...) 53 { 54 va_list ap; 55 56 fprintf (stderr, "%s:%i: %s: FAIL: ", loc.m_file, loc.m_line, 57 loc.m_function); 58 va_start (ap, fmt); 59 vfprintf (stderr, fmt, ap); 60 va_end (ap); 61 fprintf (stderr, "\n"); 62 abort (); 63 } 64 65 /* Implementation detail of ASSERT_STREQ. 66 Compare val_expected and val_actual with strcmp. They ought 67 to be non-NULL; fail gracefully if either are NULL. */ 68 69 void 70 assert_streq (const location &loc, 71 const char *desc_expected, const char *desc_actual, 72 const char *val_expected, const char *val_actual) 73 { 74 /* If val_expected is NULL, the test is buggy. Fail gracefully. */ 75 if (val_expected == NULL) 76 fail_formatted (loc, "ASSERT_STREQ (%s, %s) expected=NULL", 77 desc_expected, desc_actual); 78 /* If val_actual is NULL, fail with a custom error message. */ 79 if (val_actual == NULL) 80 fail_formatted (loc, "ASSERT_STREQ (%s, %s) expected=\"%s\" actual=NULL", 81 desc_expected, desc_actual, val_expected); 82 if (strcmp (val_expected, val_actual) == 0) 83 pass (loc, "ASSERT_STREQ"); 84 else 85 fail_formatted (loc, "ASSERT_STREQ (%s, %s) expected=\"%s\" actual=\"%s\"", 86 desc_expected, desc_actual, val_expected, val_actual); 87 } 88 89 /* Implementation detail of ASSERT_STR_CONTAINS. 90 Use strstr to determine if val_needle is is within val_haystack. 91 ::selftest::pass if it is found. 92 ::selftest::fail if it is not found. */ 93 94 void 95 assert_str_contains (const location &loc, 96 const char *desc_haystack, 97 const char *desc_needle, 98 const char *val_haystack, 99 const char *val_needle) 100 { 101 /* If val_haystack is NULL, fail with a custom error message. */ 102 if (val_haystack == NULL) 103 fail_formatted (loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=NULL", 104 desc_haystack, desc_needle); 105 106 /* If val_needle is NULL, fail with a custom error message. */ 107 if (val_needle == NULL) 108 fail_formatted (loc, 109 "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=NULL", 110 desc_haystack, desc_needle, val_haystack); 111 112 const char *test = strstr (val_haystack, val_needle); 113 if (test) 114 pass (loc, "ASSERT_STR_CONTAINS"); 115 else 116 fail_formatted 117 (loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=\"%s\"", 118 desc_haystack, desc_needle, val_haystack, val_needle); 119 } 120 121 /* Constructor. Generate a name for the file. */ 122 123 named_temp_file::named_temp_file (const char *suffix) 124 { 125 m_filename = make_temp_file (suffix); 126 ASSERT_NE (m_filename, NULL); 127 } 128 129 /* Destructor. Delete the tempfile. */ 130 131 named_temp_file::~named_temp_file () 132 { 133 unlink (m_filename); 134 diagnostics_file_cache_forcibly_evict_file (m_filename); 135 free (m_filename); 136 } 137 138 /* Constructor. Create a tempfile using SUFFIX, and write CONTENT to 139 it. Abort if anything goes wrong, using LOC as the effective 140 location in the problem report. */ 141 142 temp_source_file::temp_source_file (const location &loc, 143 const char *suffix, 144 const char *content) 145 : named_temp_file (suffix) 146 { 147 FILE *out = fopen (get_filename (), "w"); 148 if (!out) 149 fail_formatted (loc, "unable to open tempfile: %s", get_filename ()); 150 fprintf (out, "%s", content); 151 fclose (out); 152 } 153 154 /* Read the contents of PATH into memory, returning a 0-terminated buffer 155 that must be freed by the caller. 156 Fail (and abort) if there are any problems, with LOC as the reported 157 location of the failure. */ 158 159 char * 160 read_file (const location &loc, const char *path) 161 { 162 FILE *f_in = fopen (path, "r"); 163 if (!f_in) 164 fail_formatted (loc, "unable to open file: %s", path); 165 166 /* Read content, allocating FIXME. */ 167 char *result = NULL; 168 size_t total_sz = 0; 169 size_t alloc_sz = 0; 170 char buf[4096]; 171 size_t iter_sz_in; 172 173 while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) ) 174 { 175 gcc_assert (alloc_sz >= total_sz); 176 size_t old_total_sz = total_sz; 177 total_sz += iter_sz_in; 178 /* Allow 1 extra byte for 0-termination. */ 179 if (alloc_sz < (total_sz + 1)) 180 { 181 size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1; 182 result = (char *)xrealloc (result, new_alloc_sz); 183 alloc_sz = new_alloc_sz; 184 } 185 memcpy (result + old_total_sz, buf, iter_sz_in); 186 } 187 188 if (!feof (f_in)) 189 fail_formatted (loc, "error reading from %s: %s", path, 190 xstrerror (errno)); 191 192 fclose (f_in); 193 194 /* 0-terminate the buffer. */ 195 gcc_assert (total_sz < alloc_sz); 196 result[total_sz] = '\0'; 197 198 return result; 199 } 200 201 /* The path of SRCDIR/testsuite/selftests. */ 202 203 const char *path_to_selftest_files = NULL; 204 205 /* Convert a path relative to SRCDIR/testsuite/selftests 206 to a real path (either absolute, or relative to pwd). 207 The result should be freed by the caller. */ 208 209 char * 210 locate_file (const char *name) 211 { 212 ASSERT_NE (NULL, path_to_selftest_files); 213 return concat (path_to_selftest_files, "/", name, NULL); 214 } 215 216 /* selftest::test_runner's ctor. */ 217 218 test_runner::test_runner (const char *name) 219 : m_name (name), 220 m_start_time (get_run_time ()) 221 { 222 } 223 224 /* selftest::test_runner's dtor. Print a summary line to stderr. */ 225 226 test_runner::~test_runner () 227 { 228 /* Finished running tests. */ 229 long finish_time = get_run_time (); 230 long elapsed_time = finish_time - m_start_time; 231 232 fprintf (stderr, 233 "%s: %i pass(es) in %ld.%06ld seconds\n", 234 m_name, num_passes, 235 elapsed_time / 1000000, elapsed_time % 1000000); 236 } 237 238 /* Selftests for libiberty. */ 239 240 /* Verify that xstrndup generates EXPECTED when called on SRC and N. */ 241 242 static void 243 assert_xstrndup_eq (const char *expected, const char *src, size_t n) 244 { 245 char *buf = xstrndup (src, n); 246 ASSERT_STREQ (expected, buf); 247 free (buf); 248 } 249 250 /* Verify that xstrndup works as expected. */ 251 252 static void 253 test_xstrndup () 254 { 255 assert_xstrndup_eq ("", "test", 0); 256 assert_xstrndup_eq ("t", "test", 1); 257 assert_xstrndup_eq ("te", "test", 2); 258 assert_xstrndup_eq ("tes", "test", 3); 259 assert_xstrndup_eq ("test", "test", 4); 260 assert_xstrndup_eq ("test", "test", 5); 261 262 /* Test on an string without zero termination. */ 263 const char src[4] = {'t', 'e', 's', 't'}; 264 assert_xstrndup_eq ("", src, 0); 265 assert_xstrndup_eq ("t", src, 1); 266 assert_xstrndup_eq ("te", src, 2); 267 assert_xstrndup_eq ("tes", src, 3); 268 assert_xstrndup_eq ("test", src, 4); 269 } 270 271 /* Run selftests for libiberty. */ 272 273 static void 274 test_libiberty () 275 { 276 test_xstrndup (); 277 } 278 279 /* Selftests for the selftest system itself. */ 280 281 /* Sanity-check the ASSERT_ macros with various passing cases. */ 282 283 static void 284 test_assertions () 285 { 286 ASSERT_TRUE (true); 287 ASSERT_FALSE (false); 288 ASSERT_EQ (1, 1); 289 ASSERT_EQ_AT (SELFTEST_LOCATION, 1, 1); 290 ASSERT_NE (1, 2); 291 ASSERT_GT (2, 1); 292 ASSERT_GT_AT (SELFTEST_LOCATION, 2, 1); 293 ASSERT_LT (1, 2); 294 ASSERT_LT_AT (SELFTEST_LOCATION, 1, 2); 295 ASSERT_STREQ ("test", "test"); 296 ASSERT_STREQ_AT (SELFTEST_LOCATION, "test", "test"); 297 ASSERT_STR_CONTAINS ("foo bar baz", "bar"); 298 } 299 300 /* Verify named_temp_file. */ 301 302 static void 303 test_named_temp_file () 304 { 305 named_temp_file t (".txt"); 306 FILE *f = fopen (t.get_filename (), "w"); 307 if (!f) 308 fail_formatted (SELFTEST_LOCATION, 309 "unable to open %s for writing", t.get_filename ()); 310 fclose (f); 311 } 312 313 /* Verify read_file (and also temp_source_file). */ 314 315 static void 316 test_read_file () 317 { 318 temp_source_file t (SELFTEST_LOCATION, "test1.s", 319 "\tjmp\t.L2\n"); 320 char *buf = read_file (SELFTEST_LOCATION, t.get_filename ()); 321 ASSERT_STREQ ("\tjmp\t.L2\n", buf); 322 free (buf); 323 } 324 325 /* Verify locate_file (and read_file). */ 326 327 static void 328 test_locate_file () 329 { 330 char *path = locate_file ("example.txt"); 331 char *buf = read_file (SELFTEST_LOCATION, path); 332 ASSERT_STREQ ("example of a selftest file\n", buf); 333 free (buf); 334 free (path); 335 } 336 337 /* Run all of the selftests within this file. */ 338 339 void 340 selftest_c_tests () 341 { 342 test_libiberty (); 343 test_assertions (); 344 test_named_temp_file (); 345 test_read_file (); 346 test_locate_file (); 347 } 348 349 } // namespace selftest 350 351 #endif /* #if CHECKING_P */ 352