testutils.c 12.4 KB
Newer Older
K
Karel Zak 已提交
1
/*
2
 * testutils.c: basic test utils
K
Karel Zak 已提交
3
 *
4
 * Copyright (C) 2005-2009 Red Hat, Inc.
K
Karel Zak 已提交
5 6 7 8 9 10
 *
 * See COPYING.LIB for the License of this software
 *
 * Karel Zak <kzak@redhat.com>
 */

11
#include <config.h>
12

K
Karel Zak 已提交
13 14 15
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
16 17
#include <sys/types.h>
#include <sys/stat.h>
A
Atsushi SAKAI 已提交
18
#ifndef WIN32
19
#include <sys/wait.h>
A
Atsushi SAKAI 已提交
20
#endif
21
#ifdef HAVE_REGEX_H
22
#include <regex.h>
23
#endif
24
#include <unistd.h>
25
#include <string.h>
26 27
#include <fcntl.h>
#include <limits.h>
K
Karel Zak 已提交
28
#include "testutils.h"
29
#include "internal.h"
30 31
#include "memory.h"
#include "util.h"
32 33
#include "threads.h"
#include "virterror_internal.h"
34 35 36 37

#if TEST_OOM_TRACE
#include <execinfo.h>
#endif
K
Karel Zak 已提交
38

39 40 41 42
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif

K
Karel Zak 已提交
43
#define GETTIMEOFDAY(T) gettimeofday(T, NULL)
44 45 46
#define DIFF_MSEC(T, U)                                 \
    ((((int) ((T)->tv_sec - (U)->tv_sec)) * 1000000.0 +	\
      ((int) ((T)->tv_usec - (U)->tv_usec))) / 1000.0)
K
Karel Zak 已提交
47

48 49 50 51
static unsigned int testOOM = 0;
static unsigned int testDebug = 0;
static unsigned int testCounter = 0;

K
Karel Zak 已提交
52 53 54
double
virtTestCountAverage(double *items, int nitems)
{
55 56
    long double sum = 0;
    int i;
K
Karel Zak 已提交
57

58 59
    for (i=1; i < nitems; i++)
        sum += items[i];
K
Karel Zak 已提交
60

61
    return (double) (sum / nitems);
K
Karel Zak 已提交
62 63
}

64
/*
K
Karel Zak 已提交
65
 * Runs test and count average time (if the nloops is grater than 1)
66 67
 *
 * returns: -1 = error, 0 = success
K
Karel Zak 已提交
68 69
 */
int
70
virtTestRun(const char *title, int nloops, int (*body)(const void *data), const void *data)
K
Karel Zak 已提交
71
{
72 73
    int i, ret = 0;
    double *ts = NULL;
74

75
    testCounter++;
76

77 78 79 80
    if (testOOM < 2) {
        fprintf(stderr, "%2d) %-65s ... ", testCounter, title);
        fflush(stderr);
    }
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

    if (nloops > 1 && (ts = calloc(nloops,
                                   sizeof(double)))==NULL)
        return -1;

    for (i=0; i < nloops; i++) {
        struct timeval before, after;

        if (ts)
            GETTIMEOFDAY(&before);
        if ((ret = body(data)) != 0)
            break;
        if (ts)	{
            GETTIMEOFDAY(&after);
            ts[i] = DIFF_MSEC(&after, &before);
        }
    }
98 99 100 101 102 103 104 105 106
    if (testOOM < 2) {
        if (ret == 0 && ts)
            fprintf(stderr, "OK     [%.5f ms]\n",
                    virtTestCountAverage(ts, nloops));
        else if (ret == 0)
            fprintf(stderr, "OK\n");
        else
            fprintf(stderr, "FAILED\n");
    }
107

108
    free(ts);
109
    return ret;
K
Karel Zak 已提交
110
}
111

112 113 114 115 116
/* Read FILE into buffer BUF of length BUFLEN.
   Upon any failure, or if FILE appears to contain more than BUFLEN bytes,
   diagnose it and return -1, but don't bother trying to preserve errno.
   Otherwise, return the number of bytes read (and copied into BUF).  */
int virtTestLoadFile(const char *file,
117 118
                     char **buf,
                     int buflen) {
119
    FILE *fp = fopen(file, "r");
120
    struct stat st;
121

122 123
    if (!fp) {
        fprintf (stderr, "%s: failed to open: %s\n", file, strerror(errno));
124
        return -1;
125
    }
126 127

    if (fstat(fileno(fp), &st) < 0) {
128
        fprintf (stderr, "%s: failed to fstat: %s\n", file, strerror(errno));
129 130 131 132 133
        fclose(fp);
        return -1;
    }

    if (st.st_size > (buflen-1)) {
134
        fprintf (stderr, "%s: larger than buffer (> %d)\n", file, buflen-1);
135 136 137 138
        fclose(fp);
        return -1;
    }

139 140
    if (st.st_size) {
        if (fread(*buf, st.st_size, 1, fp) != 1) {
141
            fprintf (stderr, "%s: read failed: %s\n", file, strerror(errno));
142 143 144
            fclose(fp);
            return -1;
        }
145 146 147 148 149 150 151
    }
    (*buf)[st.st_size] = '\0';

    fclose(fp);
    return st.st_size;
}

A
Atsushi SAKAI 已提交
152
#ifndef WIN32
153 154
static
void virtTestCaptureProgramExecChild(const char *const argv[],
155 156 157 158 159 160
                                     int pipefd) {
    int i;
    int open_max;
    int stdinfd = -1;
    const char *const env[] = {
        "LANG=C",
161 162 163
#if WITH_DRIVER_MODULES
        "LIBVIRT_DRIVER_DIR=" TEST_DRIVER_DIR,
#endif
164 165 166
        NULL
    };

167
    if ((stdinfd = open("/dev/null", O_RDONLY)) < 0)
168 169 170 171 172 173 174 175 176 177 178 179 180
        goto cleanup;

    open_max = sysconf (_SC_OPEN_MAX);
    for (i = 0; i < open_max; i++) {
        if (i != stdinfd &&
            i != pipefd)
            close(i);
    }

    if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
        goto cleanup;
    if (dup2(pipefd, STDOUT_FILENO) != STDOUT_FILENO)
        goto cleanup;
181
    if (dup2(pipefd, STDERR_FILENO) != STDERR_FILENO)
182 183 184 185
        goto cleanup;

    /* SUS is crazy here, hence the cast */
    execve(argv[0], (char *const*)argv, (char *const*)env);
186 187

 cleanup:
188 189
    if (stdinfd != -1)
        close(stdinfd);
190 191 192
}

int virtTestCaptureProgramOutput(const char *const argv[],
193 194 195 196 197 198 199 200 201
                                 char **buf,
                                 int buflen) {
    int pipefd[2];

    if (pipe(pipefd) < 0)
        return -1;

    int pid = fork();
    switch (pid) {
202 203
    case 0:
        close(pipefd[0]);
204 205 206 207 208
        virtTestCaptureProgramExecChild(argv, pipefd[1]);

        close(pipefd[1]);
        _exit(1);

209 210
    case -1:
        return -1;
211

212
    default:
213 214 215 216
        {
            int got = 0;
            int ret = -1;
            int want = buflen-1;
217

218
            close(pipefd[1]);
219

220 221 222 223 224 225 226
            while (want) {
                if ((ret = read(pipefd[0], (*buf)+got, want)) <= 0)
                    break;
                got += ret;
                want -= ret;
            }
            close(pipefd[0]);
227

228 229
            if (!ret)
                (*buf)[got] = '\0';
230

231
            waitpid(pid, NULL, 0);
232

233 234 235
            return ret;
        }
    }
236
}
A
Atsushi SAKAI 已提交
237
#endif /* !WIN32 */
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256


/**
 * @param stream: output stream write to differences to
 * @param expect: expected output text
 * @param actual: actual output text
 *
 * Display expected and actual output text, trimmed to
 * first and last characters at which differences occur
 */
int virtTestDifference(FILE *stream,
                       const char *expect,
                       const char *actual)
{
    const char *expectStart = expect;
    const char *expectEnd = expect + (strlen(expect)-1);
    const char *actualStart = actual;
    const char *actualEnd = actual + (strlen(actual)-1);

257 258 259 260
    if (!testDebug)
        return 0;

    if (testDebug < 2) {
261 262 263 264 265 266
        /* Skip to first character where they differ */
        while (*expectStart && *actualStart &&
               *actualStart == *expectStart) {
            actualStart++;
            expectStart++;
        }
267

268 269 270 271 272 273 274
        /* Work backwards to last character where they differ */
        while (actualEnd > actualStart &&
               expectEnd > expectStart &&
               *actualEnd == *expectEnd) {
            actualEnd--;
            expectEnd--;
        }
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
    }

    /* Show the trimmed differences */
    fprintf(stream, "\nExpect [");
    if ((expectEnd - expectStart + 1) &&
        fwrite(expectStart, (expectEnd-expectStart+1), 1, stream) != 1)
        return -1;
    fprintf(stream, "]\n");
    fprintf(stream, "Actual [");
    if ((actualEnd - actualStart + 1) &&
        fwrite(actualStart, (actualEnd-actualStart+1), 1, stream) != 1)
        return -1;
    fprintf(stream, "]\n");

    /* Pad to line up with test name ... in virTestRun */
    fprintf(stream, "                                                                      ... ");

    return 0;
}
294

295
#if TEST_OOM
296 297 298 299
static void
virtTestErrorFuncQuiet(void *data ATTRIBUTE_UNUSED,
                       virErrorPtr err ATTRIBUTE_UNUSED)
{ }
300
#endif
301

302
#if TEST_OOM_TRACE
303
static void
304
virtTestErrorHook(int n, void *data ATTRIBUTE_UNUSED)
305 306 307 308 309 310 311 312 313
{
    void *trace[30];
    int ntrace = ARRAY_CARDINALITY(trace);
    int i;
    char **symbols = NULL;

    ntrace = backtrace(trace, ntrace);
    symbols = backtrace_symbols(trace, ntrace);
    if (symbols) {
314
        fprintf(stderr, "Failing allocation %d at:\n", n);
315 316 317 318 319 320 321
        for (i = 0 ; i < ntrace ; i++) {
            if (symbols[i])
                fprintf(stderr, "  TRACE:  %s\n", symbols[i]);
        }
        free(symbols);
    }
}
322
#endif
323 324 325 326 327 328


int virtTestMain(int argc,
                 char **argv,
                 int (*func)(int, char **))
{
329
    char *debugStr;
330
    int ret;
331
#if TEST_OOM
332 333
    int approxAlloc = 0;
    int n;
334
    char *oomStr = NULL;
335
    int oomCount;
336 337 338
    int mp = 0;
    pid_t *workers;
    int worker = 0;
339 340
#endif

341 342 343 344
    if (virThreadInitialize() < 0 ||
        virErrorInitialize() < 0)
        return 1;

345
    if ((debugStr = getenv("VIR_TEST_DEBUG")) != NULL) {
D
Daniel P. Berrange 已提交
346
        if (virStrToLong_ui(debugStr, NULL, 10, &testDebug) < 0)
347 348 349
            testDebug = 0;
    }

350
#if TEST_OOM
351 352 353 354 355 356 357 358 359 360
    if ((oomStr = getenv("VIR_TEST_OOM")) != NULL) {
        if (virStrToLong_i(oomStr, NULL, 10, &oomCount) < 0)
            oomCount = 0;

        if (oomCount < 0)
            oomCount = 0;
        if (oomCount)
            testOOM = 1;
    }

361 362 363
    if (getenv("VIR_TEST_MP") != NULL) {
        mp = sysconf(_SC_NPROCESSORS_ONLN);
        fprintf(stderr, "Using %d worker processes\n", mp);
364 365 366 367
        if (VIR_ALLOC_N(workers, mp) < 0) {
            ret = EXIT_FAILURE;
            goto cleanup;
        }
368 369
    }

D
Daniel P. Berrange 已提交
370
    /* Run once to prime any static allocations & ensure it passes */
371 372
    ret = (func)(argc, argv);
    if (ret != EXIT_SUCCESS)
373
        goto cleanup;
374

375
#if TEST_OOM_TRACE
376 377
    if (testDebug)
        virAllocTestHook(virtTestErrorHook, NULL);
378
#endif
379 380 381 382 383 384

    if (testOOM) {
        /* Makes next test runs quiet... */
        testOOM++;
        virSetErrorFunc(NULL, virtTestErrorFuncQuiet);

D
Daniel P. Berrange 已提交
385 386 387 388 389 390 391
        virAllocTestInit();

        /* Run again to count allocs, and ensure it passes :-) */
        ret = (func)(argc, argv);
        if (ret != EXIT_SUCCESS)
            goto cleanup;

392 393 394 395 396 397 398
        approxAlloc = virAllocTestCount();
        testCounter++;
        if (testDebug)
            fprintf(stderr, "%d) OOM...\n", testCounter);
        else
            fprintf(stderr, "%d) OOM of %d allocs ", testCounter, approxAlloc);

399 400 401 402 403 404 405 406 407 408 409
        if (mp) {
            int i;
            for (i = 0 ; i < mp ; i++) {
                workers[i] = fork();
                if (workers[i] == 0) {
                    worker = i + 1;
                    break;
                }
            }
        }

410 411
        /* Run once for each alloc, failing a different one
           and validating that the test case failed */
412
        for (n = 0; n < approxAlloc && (!mp || worker) ; n++) {
413 414
            if (mp &&
                (n % mp) != (worker - 1))
415
                continue;
416
            if (!testDebug) {
417 418 419 420
                if (mp)
                    fprintf(stderr, "%d", worker);
                else
                    fprintf(stderr, ".");
421 422 423 424 425 426 427 428 429 430
                fflush(stderr);
            }
            virAllocTestOOM(n+1, oomCount);

            if (((func)(argc, argv)) != EXIT_FAILURE) {
                ret = EXIT_FAILURE;
                break;
            }
        }

431 432 433 434 435 436 437 438 439 440 441 442 443 444
        if (mp) {
            if (worker) {
                _exit(ret);
            } else {
                int i, status;
                for (i = 0 ; i < mp ; i++) {
                    waitpid(workers[i], &status, 0);
                    if (WEXITSTATUS(status) != EXIT_SUCCESS)
                        ret = EXIT_FAILURE;
                }
                VIR_FREE(workers);
            }
        }

445 446 447 448 449 450 451 452
        if (testDebug)
            fprintf(stderr, " ... OOM of %d allocs", approxAlloc);

        if (ret == EXIT_SUCCESS)
            fprintf(stderr, " OK\n");
        else
            fprintf(stderr, " FAILED\n");
    }
453
cleanup:
454
#else
455
    ret = (func)(argc, argv);
456
#endif
457 458 459

    virResetLastError();
    return ret;
460
}
461 462


463
#ifdef HAVE_REGEX_H
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
int virtTestClearLineRegex(const char *pattern,
                           char *str)
{
    regex_t reg;
    char *lineStart = str;
    char *lineEnd = strchr(str, '\n');

    if (regcomp(&reg, pattern, REG_EXTENDED | REG_NOSUB) != 0)
        return -1;

    while (lineStart) {
        int ret;
        if (lineEnd)
            *lineEnd = '\0';


        ret = regexec(&reg, lineStart, 0, NULL, 0);
        //fprintf(stderr, "Match %d '%s' '%s'\n", ret, lineStart, pattern);
        if (ret == 0) {
            if (lineEnd) {
                memmove(lineStart, lineEnd + 1, strlen(lineEnd+1) + 1);
                /* Don't update lineStart - just iterate again on this
                   location */
                lineEnd = strchr(lineStart, '\n');
            } else {
                *lineStart = '\0';
                lineStart = NULL;
            }
        } else {
            if (lineEnd) {
                *lineEnd = '\n';
                lineStart = lineEnd + 1;
                lineEnd = strchr(lineStart, '\n');
            } else {
                lineStart = NULL;
            }
        }
    }

    regfree(&reg);

    return 0;
}
507 508 509 510 511 512 513
#else
int virtTestClearLineRegex(const char *pattern ATTRIBUTE_UNUSED,
                           char *str ATTRIBUTE_UNUSED)
{
    return 0;
}
#endif