selftest.c 10.6 KB
Newer Older
David Malcolm committed
1
/* A self-testing framework, for use by -fself-test.
2
   Copyright (C) 2015-2019 Free Software Foundation, Inc.
David Malcolm committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.

GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "selftest.h"
24
#include "intl.h"
David Malcolm committed
25 26 27

#if CHECKING_P

28 29 30
namespace selftest {

int num_passes;
David Malcolm committed
31 32 33 34

/* Record the successful outcome of some aspect of a test.  */

void
35
pass (const location &/*loc*/, const char */*msg*/)
David Malcolm committed
36 37 38 39 40 41 42
{
  num_passes++;
}

/* Report the failed outcome of some aspect of a test and abort.  */

void
43
fail (const location &loc, const char *msg)
David Malcolm committed
44
{
45 46
  fprintf (stderr,"%s:%i: %s: FAIL: %s\n", loc.m_file, loc.m_line,
	   loc.m_function, msg);
David Malcolm committed
47 48 49
  abort ();
}

50 51 52
/* As "fail", but using printf-style formatted output.  */

void
53
fail_formatted (const location &loc, const char *fmt, ...)
54 55 56
{
  va_list ap;

57 58
  fprintf (stderr, "%s:%i: %s: FAIL: ", loc.m_file, loc.m_line,
	   loc.m_function);
59 60 61 62 63 64 65
  va_start (ap, fmt);
  vfprintf (stderr, fmt, ap);
  va_end (ap);
  fprintf (stderr, "\n");
  abort ();
}

66
/* Implementation detail of ASSERT_STREQ.
67 68
   Compare val1 and val2 with strcmp.  They ought
   to be non-NULL; fail gracefully if either or both are NULL.  */
69 70

void
71
assert_streq (const location &loc,
72 73
	      const char *desc_val1, const char *desc_val2,
	      const char *val1, const char *val2)
74
{
75 76 77 78 79 80 81 82
  /* If val1 or val2 are NULL, fail with a custom error message.  */
  if (val1 == NULL)
    if (val2 == NULL)
      fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=NULL",
		      desc_val1, desc_val2);
    else
      fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=NULL val2=\"%s\"",
		      desc_val1, desc_val2, val2);
83
  else
84 85 86 87 88 89 90 91 92 93 94
    if (val2 == NULL)
      fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=\"%s\" val2=NULL",
		      desc_val1, desc_val2, val1);
    else
      {
	if (strcmp (val1, val2) == 0)
	  pass (loc, "ASSERT_STREQ");
	else
	  fail_formatted (loc, "ASSERT_STREQ (%s, %s) val1=\"%s\" val2=\"%s\"",
			  desc_val1, desc_val2, val1, val2);
      }
95 96
}

97 98 99 100 101 102
/* Implementation detail of ASSERT_STR_CONTAINS.
   Use strstr to determine if val_needle is is within val_haystack.
   ::selftest::pass if it is found.
   ::selftest::fail if it is not found.  */

void
103 104 105 106 107
assert_str_contains (const location &loc,
		     const char *desc_haystack,
		     const char *desc_needle,
		     const char *val_haystack,
		     const char *val_needle)
108 109 110
{
  /* If val_haystack is NULL, fail with a custom error message.  */
  if (val_haystack == NULL)
111 112
    fail_formatted (loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=NULL",
		    desc_haystack, desc_needle);
113 114 115

  /* If val_needle is NULL, fail with a custom error message.  */
  if (val_needle == NULL)
116 117 118
    fail_formatted (loc,
		    "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=NULL",
		    desc_haystack, desc_needle, val_haystack);
119 120 121

  const char *test = strstr (val_haystack, val_needle);
  if (test)
122
    pass (loc, "ASSERT_STR_CONTAINS");
123
  else
124
    fail_formatted
125 126 127 128
	(loc, "ASSERT_STR_CONTAINS (%s, %s) haystack=\"%s\" needle=\"%s\"",
	 desc_haystack, desc_needle, val_haystack, val_needle);
}

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
/* Implementation detail of ASSERT_STR_STARTSWITH.
   Determine if VAL_STR starts with VAL_PREFIX.
   ::selftest::pass if VAL_STR does start with VAL_PREFIX.
   ::selftest::fail if it does not, or either is NULL (using
   DESC_STR and DESC_PREFIX in the error message).  */

void
assert_str_startswith (const location &loc,
		       const char *desc_str,
		       const char *desc_prefix,
		       const char *val_str,
		       const char *val_prefix)
{
  /* If val_str is NULL, fail with a custom error message.  */
  if (val_str == NULL)
    fail_formatted (loc, "ASSERT_STR_STARTSWITH (%s, %s) str=NULL",
		    desc_str, desc_prefix);

  /* If val_prefix is NULL, fail with a custom error message.  */
  if (val_prefix == NULL)
    fail_formatted (loc,
		    "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=NULL",
		    desc_str, desc_prefix, val_str);

  const char *test = strstr (val_str, val_prefix);
  if (test == val_str)
    pass (loc, "ASSERT_STR_STARTSWITH");
  else
    fail_formatted
	(loc, "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=\"%s\"",
	 desc_str, desc_prefix, val_str, val_prefix);
}


163
/* Constructor.  Generate a name for the file.  */
164

165
named_temp_file::named_temp_file (const char *suffix)
166 167 168 169 170 171 172
{
  m_filename = make_temp_file (suffix);
  ASSERT_NE (m_filename, NULL);
}

/* Destructor.  Delete the tempfile.  */

173
named_temp_file::~named_temp_file ()
174 175
{
  unlink (m_filename);
176
  diagnostics_file_cache_forcibly_evict_file (m_filename);
177 178
  free (m_filename);
}
179

180 181 182 183
/* Constructor.  Create a tempfile using SUFFIX, and write CONTENT to
   it.  Abort if anything goes wrong, using LOC as the effective
   location in the problem report.  */

184 185 186
temp_source_file::temp_source_file (const location &loc,
				    const char *suffix,
				    const char *content)
187 188 189 190
: named_temp_file (suffix)
{
  FILE *out = fopen (get_filename (), "w");
  if (!out)
191
    fail_formatted (loc, "unable to open tempfile: %s", get_filename ());
192 193 194 195
  fprintf (out, "%s", content);
  fclose (out);
}

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
/* Avoid introducing locale-specific differences in the results
   by hardcoding open_quote and close_quote.  */

auto_fix_quotes::auto_fix_quotes ()
{
  m_saved_open_quote = open_quote;
  m_saved_close_quote = close_quote;
  open_quote = "`";
  close_quote = "'";
}

/* Restore old values of open_quote and close_quote.  */

auto_fix_quotes::~auto_fix_quotes ()
{
  open_quote = m_saved_open_quote;
  close_quote = m_saved_close_quote;
}

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
/* Read the contents of PATH into memory, returning a 0-terminated buffer
   that must be freed by the caller.
   Fail (and abort) if there are any problems, with LOC as the reported
   location of the failure.  */

char *
read_file (const location &loc, const char *path)
{
  FILE *f_in = fopen (path, "r");
  if (!f_in)
    fail_formatted (loc, "unable to open file: %s", path);

  /* Read content, allocating FIXME.  */
  char *result = NULL;
  size_t total_sz = 0;
  size_t alloc_sz = 0;
  char buf[4096];
  size_t iter_sz_in;

  while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
    {
      gcc_assert (alloc_sz >= total_sz);
      size_t old_total_sz = total_sz;
      total_sz += iter_sz_in;
      /* Allow 1 extra byte for 0-termination.  */
      if (alloc_sz < (total_sz + 1))
	{
	  size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1;
	  result = (char *)xrealloc (result, new_alloc_sz);
	  alloc_sz = new_alloc_sz;
	}
      memcpy (result + old_total_sz, buf, iter_sz_in);
    }

  if (!feof (f_in))
    fail_formatted (loc, "error reading from %s: %s", path,
		    xstrerror (errno));

  fclose (f_in);

  /* 0-terminate the buffer.  */
  gcc_assert (total_sz < alloc_sz);
  result[total_sz] = '\0';

  return result;
}

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
/* The path of SRCDIR/testsuite/selftests.  */

const char *path_to_selftest_files = NULL;

/* Convert a path relative to SRCDIR/testsuite/selftests
   to a real path (either absolute, or relative to pwd).
   The result should be freed by the caller.  */

char *
locate_file (const char *name)
{
  ASSERT_NE (NULL, path_to_selftest_files);
  return concat (path_to_selftest_files, "/", name, NULL);
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
/* selftest::test_runner's ctor.  */

test_runner::test_runner (const char *name)
: m_name (name),
  m_start_time (get_run_time ())
{
}

/* selftest::test_runner's dtor.  Print a summary line to stderr.  */

test_runner::~test_runner ()
{
  /* Finished running tests.  */
  long finish_time = get_run_time ();
  long elapsed_time = finish_time - m_start_time;

  fprintf (stderr,
	   "%s: %i pass(es) in %ld.%06ld seconds\n",
	   m_name, num_passes,
	   elapsed_time / 1000000, elapsed_time % 1000000);
}

299 300
/* Selftests for libiberty.  */

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 308 309 310
  ASSERT_STREQ (expected, buf);
  free (buf);
}

311
/* Verify that xstrndup works as expected.  */
312 313

static void
314
test_xstrndup ()
315
{
316 317 318 319 320 321
  assert_xstrndup_eq ("", "test", 0);
  assert_xstrndup_eq ("t", "test", 1);
  assert_xstrndup_eq ("te", "test", 2);
  assert_xstrndup_eq ("tes", "test", 3);
  assert_xstrndup_eq ("test", "test", 4);
  assert_xstrndup_eq ("test", "test", 5);
322 323 324

  /* Test on an string without zero termination.  */
  const char src[4] = {'t', 'e', 's', 't'};
325 326 327 328 329
  assert_xstrndup_eq ("", src, 0);
  assert_xstrndup_eq ("t", src, 1);
  assert_xstrndup_eq ("te", src, 2);
  assert_xstrndup_eq ("tes", src, 3);
  assert_xstrndup_eq ("test", src, 4);
330 331 332 333 334 335 336
}

/* Run selftests for libiberty.  */

static void
test_libiberty ()
{
337
  test_xstrndup ();
338 339
}

340 341 342 343 344 345 346 347 348 349 350 351
/* Selftests for the selftest system itself.  */

/* Sanity-check the ASSERT_ macros with various passing cases.  */

static void
test_assertions ()
{
  ASSERT_TRUE (true);
  ASSERT_FALSE (false);
  ASSERT_EQ (1, 1);
  ASSERT_EQ_AT (SELFTEST_LOCATION, 1, 1);
  ASSERT_NE (1, 2);
352 353 354 355
  ASSERT_GT (2, 1);
  ASSERT_GT_AT (SELFTEST_LOCATION, 2, 1);
  ASSERT_LT (1, 2);
  ASSERT_LT_AT (SELFTEST_LOCATION, 1, 2);
356 357
  ASSERT_STREQ ("test", "test");
  ASSERT_STREQ_AT (SELFTEST_LOCATION, "test", "test");
358
  ASSERT_STR_CONTAINS ("foo bar baz", "bar");
359 360
}

361 362 363 364 365 366 367 368
/* Verify named_temp_file.  */

static void
test_named_temp_file ()
{
  named_temp_file t (".txt");
  FILE *f = fopen (t.get_filename (), "w");
  if (!f)
369 370
    fail_formatted (SELFTEST_LOCATION,
		    "unable to open %s for writing", t.get_filename ());
371 372 373
  fclose (f);
}

374 375 376 377 378 379 380 381 382 383 384 385
/* Verify read_file (and also temp_source_file).  */

static void
test_read_file ()
{
  temp_source_file t (SELFTEST_LOCATION, "test1.s",
		      "\tjmp\t.L2\n");
  char *buf = read_file (SELFTEST_LOCATION, t.get_filename ());
  ASSERT_STREQ ("\tjmp\t.L2\n", buf);
  free (buf);
}

386 387 388 389 390 391 392 393 394 395 396 397
/* Verify locate_file (and read_file).  */

static void
test_locate_file ()
{
  char *path = locate_file ("example.txt");
  char *buf = read_file (SELFTEST_LOCATION, path);
  ASSERT_STREQ ("example of a selftest file\n", buf);
  free (buf);
  free (path);
}

398 399 400 401 402
/* Run all of the selftests within this file.  */

void
selftest_c_tests ()
{
403
  test_libiberty ();
404
  test_assertions ();
405
  test_named_temp_file ();
406
  test_read_file ();
407
  test_locate_file ();
408 409 410 411
}

} // namespace selftest

David Malcolm committed
412
#endif /* #if CHECKING_P */