2008-10-16 11:56:19 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2006 Red Hat, Inc.
|
|
|
|
|
*
|
|
|
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
|
|
|
* and its documentation for any purpose is hereby granted without
|
|
|
|
|
* fee, provided that the above copyright notice appear in all copies
|
|
|
|
|
* and that both that copyright notice and this permission notice
|
|
|
|
|
* appear in supporting documentation, and that the name of the
|
|
|
|
|
* copyright holders not be used in advertising or publicity
|
|
|
|
|
* pertaining to distribution of the software without specific,
|
|
|
|
|
* written prior permission. The copyright holders make no
|
|
|
|
|
* representations about the suitability of this software for any
|
|
|
|
|
* purpose. It is provided "as is" without express or implied
|
|
|
|
|
* warranty.
|
|
|
|
|
*
|
|
|
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
|
|
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
|
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
|
|
|
|
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|
|
|
|
* SOFTWARE.
|
|
|
|
|
*
|
|
|
|
|
* Authors: Carl Worth <cworth@cworth.org>
|
|
|
|
|
*/
|
|
|
|
|
|
2011-08-30 16:16:04 +02:00
|
|
|
#include "cairo-missing.h"
|
2008-10-16 11:56:19 +01:00
|
|
|
#include "cairo-perf.h"
|
|
|
|
|
#include "cairo-stats.h"
|
|
|
|
|
|
|
|
|
|
/* We use _GNU_SOURCE for getline and strndup if available. */
|
|
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
|
# define _GNU_SOURCE
|
|
|
|
|
#endif
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#ifdef HAVE_LIBGEN_H
|
|
|
|
|
#include <libgen.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
static long long
|
2010-06-24 14:59:18 +03:00
|
|
|
strtoll (const char *nptr,
|
|
|
|
|
char **endptr,
|
|
|
|
|
int base);
|
2008-10-16 11:56:19 +01:00
|
|
|
|
|
|
|
|
static char *
|
2010-06-24 14:59:18 +03:00
|
|
|
basename (char *path);
|
2008-10-16 11:56:19 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Ad-hoc parsing, macros with a strong dependence on the calling
|
|
|
|
|
* context, and plenty of other ugliness is here. But at least it's
|
|
|
|
|
* not perl... */
|
|
|
|
|
#define parse_error(...) fprintf(stderr, __VA_ARGS__); return TEST_REPORT_STATUS_ERROR;
|
|
|
|
|
#define skip_char(c) \
|
|
|
|
|
do { \
|
|
|
|
|
if (*s && *s == (c)) { \
|
|
|
|
|
s++; \
|
|
|
|
|
} else { \
|
|
|
|
|
parse_error ("expected '%c' but found '%c'", c, *s); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
#define skip_space() while (*s && (*s == ' ' || *s == '\t')) s++;
|
|
|
|
|
#define parse_int(result) \
|
|
|
|
|
do { \
|
|
|
|
|
(result) = strtol (s, &end, 10); \
|
|
|
|
|
if (*s && end != s) { \
|
|
|
|
|
s = end; \
|
|
|
|
|
} else { \
|
|
|
|
|
parse_error("expected integer but found %s", s); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
2010-06-24 14:59:18 +03:00
|
|
|
#define parse_long_long(result) \
|
2008-10-16 11:56:19 +01:00
|
|
|
do { \
|
|
|
|
|
(result) = strtoll (s, &end, 10); \
|
|
|
|
|
if (*s && end != s) { \
|
|
|
|
|
s = end; \
|
|
|
|
|
} else { \
|
|
|
|
|
parse_error("expected integer but found %s", s); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
#define parse_double(result) \
|
|
|
|
|
do { \
|
|
|
|
|
(result) = strtod (s, &end); \
|
|
|
|
|
if (*s && end != s) { \
|
|
|
|
|
s = end; \
|
|
|
|
|
} else { \
|
|
|
|
|
parse_error("expected floating-point value but found %s", s); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
/* Here a string is simply a sequence of non-whitespace */
|
|
|
|
|
#define parse_string(result) \
|
|
|
|
|
do { \
|
|
|
|
|
for (end = s; *end; end++) \
|
|
|
|
|
if (isspace (*end)) \
|
|
|
|
|
break; \
|
|
|
|
|
(result) = strndup (s, end - s); \
|
|
|
|
|
if ((result) == NULL) { \
|
|
|
|
|
fprintf (stderr, "Out of memory.\n"); \
|
|
|
|
|
exit (1); \
|
|
|
|
|
} \
|
|
|
|
|
s = end; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
static test_report_status_t
|
2010-06-24 14:59:18 +03:00
|
|
|
test_report_parse (test_report_t *report,
|
2011-07-30 17:28:21 +01:00
|
|
|
int fileno,
|
2010-06-24 14:59:18 +03:00
|
|
|
char *line,
|
|
|
|
|
char *configuration)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
char *end;
|
|
|
|
|
char *s = line;
|
|
|
|
|
cairo_bool_t is_raw = FALSE;
|
|
|
|
|
double min_time, median_time;
|
|
|
|
|
|
|
|
|
|
/* The code here looks funny unless you understand that these are
|
|
|
|
|
* all macro calls, (and then the code just looks sick). */
|
|
|
|
|
if (*s == '\n')
|
|
|
|
|
return TEST_REPORT_STATUS_COMMENT;
|
|
|
|
|
|
|
|
|
|
skip_char ('[');
|
|
|
|
|
skip_space ();
|
|
|
|
|
if (*s == '#')
|
|
|
|
|
return TEST_REPORT_STATUS_COMMENT;
|
|
|
|
|
if (*s == '*') {
|
|
|
|
|
s++;
|
|
|
|
|
is_raw = TRUE;
|
|
|
|
|
} else {
|
|
|
|
|
parse_int (report->id);
|
|
|
|
|
}
|
|
|
|
|
skip_char (']');
|
|
|
|
|
|
|
|
|
|
skip_space ();
|
|
|
|
|
|
2011-07-30 17:28:21 +01:00
|
|
|
report->fileno = fileno;
|
2008-10-16 11:56:19 +01:00
|
|
|
report->configuration = configuration;
|
|
|
|
|
parse_string (report->backend);
|
2009-05-30 16:50:17 +01:00
|
|
|
end = strrchr (report->backend, '.');
|
2009-07-28 13:02:10 +01:00
|
|
|
if (end)
|
2008-10-16 11:56:19 +01:00
|
|
|
*end++ = '\0';
|
2009-07-28 13:02:10 +01:00
|
|
|
report->content = end ? end : xstrdup ("???");
|
2008-10-16 11:56:19 +01:00
|
|
|
|
|
|
|
|
skip_space ();
|
|
|
|
|
|
|
|
|
|
parse_string (report->name);
|
2009-05-30 16:50:17 +01:00
|
|
|
end = strrchr (report->name, '.');
|
2009-07-28 13:02:10 +01:00
|
|
|
if (end)
|
2008-10-16 11:56:19 +01:00
|
|
|
*end++ = '\0';
|
2009-07-28 13:02:10 +01:00
|
|
|
report->size = end ? atoi (end) : 0;
|
2008-10-16 11:56:19 +01:00
|
|
|
|
|
|
|
|
skip_space ();
|
|
|
|
|
|
|
|
|
|
report->samples = NULL;
|
|
|
|
|
report->samples_size = 0;
|
|
|
|
|
report->samples_count = 0;
|
|
|
|
|
|
|
|
|
|
if (is_raw) {
|
|
|
|
|
parse_double (report->stats.ticks_per_ms);
|
|
|
|
|
skip_space ();
|
|
|
|
|
|
|
|
|
|
report->samples_size = 5;
|
2011-08-31 17:55:07 +02:00
|
|
|
report->samples = xmalloc (report->samples_size * sizeof (cairo_time_t));
|
2009-05-30 16:50:17 +01:00
|
|
|
report->stats.min_ticks = 0;
|
2008-10-16 11:56:19 +01:00
|
|
|
do {
|
|
|
|
|
if (report->samples_count == report->samples_size) {
|
|
|
|
|
report->samples_size *= 2;
|
|
|
|
|
report->samples = xrealloc (report->samples,
|
2011-08-31 17:55:07 +02:00
|
|
|
report->samples_size * sizeof (cairo_time_t));
|
2008-10-16 11:56:19 +01:00
|
|
|
}
|
2009-05-30 16:50:17 +01:00
|
|
|
parse_long_long (report->samples[report->samples_count]);
|
|
|
|
|
if (report->samples_count == 0) {
|
|
|
|
|
report->stats.min_ticks =
|
|
|
|
|
report->samples[report->samples_count];
|
|
|
|
|
} else if (report->stats.min_ticks >
|
|
|
|
|
report->samples[report->samples_count]){
|
|
|
|
|
report->stats.min_ticks =
|
|
|
|
|
report->samples[report->samples_count];
|
|
|
|
|
}
|
|
|
|
|
report->samples_count++;
|
2008-10-16 11:56:19 +01:00
|
|
|
skip_space ();
|
|
|
|
|
} while (*s && *s != '\n');
|
|
|
|
|
report->stats.iterations = 0;
|
|
|
|
|
skip_char ('\n');
|
|
|
|
|
} else {
|
|
|
|
|
parse_double (report->stats.min_ticks);
|
|
|
|
|
skip_space ();
|
|
|
|
|
|
|
|
|
|
parse_double (min_time);
|
|
|
|
|
report->stats.ticks_per_ms = report->stats.min_ticks / min_time;
|
|
|
|
|
|
|
|
|
|
skip_space ();
|
|
|
|
|
|
|
|
|
|
parse_double (median_time);
|
|
|
|
|
report->stats.median_ticks = median_time * report->stats.ticks_per_ms;
|
|
|
|
|
|
|
|
|
|
skip_space ();
|
|
|
|
|
|
|
|
|
|
parse_double (report->stats.std_dev);
|
|
|
|
|
report->stats.std_dev /= 100.0;
|
|
|
|
|
skip_char ('%');
|
|
|
|
|
|
|
|
|
|
skip_space ();
|
|
|
|
|
|
|
|
|
|
parse_int (report->stats.iterations);
|
|
|
|
|
|
|
|
|
|
skip_space ();
|
|
|
|
|
skip_char ('\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TEST_REPORT_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We provide hereafter a win32 implementation of the basename
|
|
|
|
|
* and strtoll functions which are not available otherwise.
|
|
|
|
|
* The basename function is fully compliant to its GNU specs.
|
|
|
|
|
*/
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
long long
|
2010-06-24 14:59:18 +03:00
|
|
|
strtoll (const char *nptr,
|
|
|
|
|
char **endptr,
|
|
|
|
|
int base)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
return _atoi64(nptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
2010-06-24 14:59:18 +03:00
|
|
|
basename (char *path)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
char *end, *s;
|
|
|
|
|
|
|
|
|
|
end = (path + strlen(path) - 1);
|
|
|
|
|
while (end && (end >= path + 1) && (*end == '/')) {
|
|
|
|
|
*end = '\0';
|
|
|
|
|
end--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s = strrchr(path, '/');
|
|
|
|
|
if (s) {
|
|
|
|
|
if (s == end) {
|
|
|
|
|
return s;
|
|
|
|
|
} else {
|
|
|
|
|
return s+1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* ifndef _MSC_VER */
|
|
|
|
|
|
|
|
|
|
int
|
2010-06-24 14:59:18 +03:00
|
|
|
test_report_cmp_backend_then_name (const void *a,
|
|
|
|
|
const void *b)
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
const test_report_t *a_test = a;
|
|
|
|
|
const test_report_t *b_test = b;
|
|
|
|
|
|
|
|
|
|
int cmp;
|
|
|
|
|
|
|
|
|
|
cmp = strcmp (a_test->backend, b_test->backend);
|
|
|
|
|
if (cmp)
|
|
|
|
|
return cmp;
|
|
|
|
|
|
|
|
|
|
cmp = strcmp (a_test->content, b_test->content);
|
|
|
|
|
if (cmp)
|
|
|
|
|
return cmp;
|
|
|
|
|
|
|
|
|
|
/* A NULL name is a list-termination marker, so force it last. */
|
|
|
|
|
if (a_test->name == NULL)
|
|
|
|
|
if (b_test->name == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
else if (b_test->name == NULL)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
cmp = strcmp (a_test->name, b_test->name);
|
|
|
|
|
if (cmp)
|
|
|
|
|
return cmp;
|
|
|
|
|
|
|
|
|
|
if (a_test->size < b_test->size)
|
|
|
|
|
return -1;
|
|
|
|
|
if (a_test->size > b_test->size)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-14 13:59:28 +00:00
|
|
|
int
|
2010-06-24 14:59:18 +03:00
|
|
|
test_report_cmp_name (const void *a,
|
|
|
|
|
const void *b)
|
2009-01-14 13:59:28 +00:00
|
|
|
{
|
|
|
|
|
const test_report_t *a_test = a;
|
|
|
|
|
const test_report_t *b_test = b;
|
|
|
|
|
|
|
|
|
|
int cmp;
|
|
|
|
|
|
|
|
|
|
/* A NULL name is a list-termination marker, so force it last. */
|
|
|
|
|
if (a_test->name == NULL)
|
|
|
|
|
if (b_test->name == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return 1;
|
|
|
|
|
else if (b_test->name == NULL)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
cmp = strcmp (a_test->name, b_test->name);
|
|
|
|
|
if (cmp)
|
|
|
|
|
return cmp;
|
|
|
|
|
|
|
|
|
|
if (a_test->size < b_test->size)
|
|
|
|
|
return -1;
|
|
|
|
|
if (a_test->size > b_test->size)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-16 11:56:19 +01:00
|
|
|
void
|
2009-01-14 13:59:28 +00:00
|
|
|
cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report,
|
2010-06-24 14:59:18 +03:00
|
|
|
int (*cmp) (const void*, const void*))
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
test_report_t *base, *next, *last, *t;
|
|
|
|
|
|
2009-01-14 13:59:28 +00:00
|
|
|
if (cmp == NULL)
|
|
|
|
|
cmp = test_report_cmp_backend_then_name;
|
|
|
|
|
|
2008-10-16 11:56:19 +01:00
|
|
|
/* First we sort, since the diff needs both lists in the same
|
|
|
|
|
* order */
|
2009-01-14 13:59:28 +00:00
|
|
|
qsort (report->tests, report->tests_count, sizeof (test_report_t), cmp);
|
2008-10-16 11:56:19 +01:00
|
|
|
|
|
|
|
|
/* The sorting also brings all related raw reports together so we
|
|
|
|
|
* can condense them and compute the stats.
|
|
|
|
|
*/
|
|
|
|
|
base = &report->tests[0];
|
|
|
|
|
last = &report->tests[report->tests_count - 1];
|
|
|
|
|
while (base <= last) {
|
|
|
|
|
next = base+1;
|
|
|
|
|
if (next <= last) {
|
|
|
|
|
while (next <= last &&
|
|
|
|
|
test_report_cmp_backend_then_name (base, next) == 0)
|
|
|
|
|
{
|
|
|
|
|
next++;
|
|
|
|
|
}
|
|
|
|
|
if (next != base) {
|
|
|
|
|
unsigned int new_samples_count = base->samples_count;
|
|
|
|
|
for (t = base + 1; t < next; t++)
|
|
|
|
|
new_samples_count += t->samples_count;
|
|
|
|
|
if (new_samples_count > base->samples_size) {
|
|
|
|
|
base->samples_size = new_samples_count;
|
|
|
|
|
base->samples = xrealloc (base->samples,
|
2011-08-31 17:55:07 +02:00
|
|
|
base->samples_size * sizeof (cairo_time_t));
|
2008-10-16 11:56:19 +01:00
|
|
|
}
|
|
|
|
|
for (t = base + 1; t < next; t++) {
|
|
|
|
|
memcpy (&base->samples[base->samples_count], t->samples,
|
2011-08-31 17:55:07 +02:00
|
|
|
t->samples_count * sizeof (cairo_time_t));
|
2008-10-16 11:56:19 +01:00
|
|
|
base->samples_count += t->samples_count;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (base->samples)
|
|
|
|
|
_cairo_stats_compute (&base->stats, base->samples, base->samples_count);
|
|
|
|
|
base = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
cairo_perf_report_load (cairo_perf_report_t *report,
|
2011-07-30 17:28:21 +01:00
|
|
|
const char *filename, int id,
|
2009-01-14 13:59:28 +00:00
|
|
|
int (*cmp) (const void *, const void *))
|
2008-10-16 11:56:19 +01:00
|
|
|
{
|
|
|
|
|
FILE *file;
|
|
|
|
|
test_report_status_t status;
|
|
|
|
|
int line_number = 0;
|
|
|
|
|
char *line = NULL;
|
|
|
|
|
size_t line_size = 0;
|
|
|
|
|
char *configuration;
|
|
|
|
|
char *dot;
|
|
|
|
|
char *baseName;
|
2009-09-24 16:30:57 +01:00
|
|
|
const char *name;
|
2008-10-16 11:56:19 +01:00
|
|
|
|
2009-09-24 16:30:57 +01:00
|
|
|
name = filename;
|
|
|
|
|
if (name == NULL)
|
|
|
|
|
name = "stdin";
|
|
|
|
|
|
|
|
|
|
configuration = xmalloc (strlen (name) * sizeof (char) + 1);
|
|
|
|
|
strcpy (configuration, name);
|
2008-12-20 19:14:23 +00:00
|
|
|
baseName = basename (configuration);
|
|
|
|
|
report->configuration = xmalloc (strlen (baseName) * sizeof (char) + 1);
|
|
|
|
|
strcpy (report->configuration, baseName);
|
2008-10-16 11:56:19 +01:00
|
|
|
free (configuration);
|
2008-12-20 19:14:23 +00:00
|
|
|
|
2008-10-16 11:56:19 +01:00
|
|
|
dot = strrchr (report->configuration, '.');
|
|
|
|
|
if (dot)
|
|
|
|
|
*dot = '\0';
|
|
|
|
|
|
2009-09-24 16:30:57 +01:00
|
|
|
report->name = name;
|
2008-10-16 11:56:19 +01:00
|
|
|
report->tests_size = 16;
|
|
|
|
|
report->tests = xmalloc (report->tests_size * sizeof (test_report_t));
|
|
|
|
|
report->tests_count = 0;
|
2011-07-30 17:28:21 +01:00
|
|
|
report->fileno = id;
|
2008-10-16 11:56:19 +01:00
|
|
|
|
2009-09-24 16:30:57 +01:00
|
|
|
if (filename == NULL) {
|
|
|
|
|
file = stdin;
|
|
|
|
|
} else {
|
|
|
|
|
file = fopen (filename, "r");
|
|
|
|
|
if (file == NULL) {
|
|
|
|
|
fprintf (stderr, "Failed to open %s: %s\n",
|
|
|
|
|
filename, strerror (errno));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
2008-10-16 11:56:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
if (report->tests_count == report->tests_size) {
|
|
|
|
|
report->tests_size *= 2;
|
|
|
|
|
report->tests = xrealloc (report->tests,
|
|
|
|
|
report->tests_size * sizeof (test_report_t));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
line_number++;
|
|
|
|
|
if (getline (&line, &line_size, file) == -1)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
status = test_report_parse (&report->tests[report->tests_count],
|
2011-07-30 17:28:21 +01:00
|
|
|
id, line, report->configuration);
|
2008-10-16 11:56:19 +01:00
|
|
|
if (status == TEST_REPORT_STATUS_ERROR)
|
|
|
|
|
fprintf (stderr, "Ignoring unrecognized line %d of %s:\n%s",
|
|
|
|
|
line_number, filename, line);
|
|
|
|
|
if (status == TEST_REPORT_STATUS_SUCCESS)
|
|
|
|
|
report->tests_count++;
|
|
|
|
|
/* Do nothing on TEST_REPORT_STATUS_COMMENT */
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-30 11:04:16 +02:00
|
|
|
free (line);
|
2008-10-16 11:56:19 +01:00
|
|
|
|
2009-09-24 16:30:57 +01:00
|
|
|
if (filename != NULL)
|
|
|
|
|
fclose (file);
|
2008-10-16 11:56:19 +01:00
|
|
|
|
2009-01-14 13:59:28 +00:00
|
|
|
cairo_perf_report_sort_and_compute_stats (report, cmp);
|
2008-10-16 11:56:19 +01:00
|
|
|
|
|
|
|
|
/* Add one final report with a NULL name to terminate the list. */
|
|
|
|
|
if (report->tests_count == report->tests_size) {
|
|
|
|
|
report->tests_size *= 2;
|
|
|
|
|
report->tests = xrealloc (report->tests,
|
|
|
|
|
report->tests_size * sizeof (test_report_t));
|
|
|
|
|
}
|
|
|
|
|
report->tests[report->tests_count].name = NULL;
|
|
|
|
|
}
|