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
unsigned int testDebug = 0;

50 51 52
static unsigned int testOOM = 0;
static unsigned int testCounter = 0;

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

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

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

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

76
    testCounter++;
77

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

    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);
        }
    }
99 100 101 102 103 104 105 106 107
    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");
    }
108

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

113 114 115 116 117
/* 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,
118 119
                     char **buf,
                     int buflen) {
120
    FILE *fp = fopen(file, "r");
121
    struct stat st;
122

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

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

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

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

    fclose(fp);
    return st.st_size;
}

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

168
    if ((stdinfd = open("/dev/null", O_RDONLY)) < 0)
169 170 171 172 173 174 175 176 177 178 179 180 181
        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;
182
    if (dup2(pipefd, STDERR_FILENO) != STDERR_FILENO)
183 184 185 186
        goto cleanup;

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

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

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

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

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

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

210 211
    case -1:
        return -1;
212

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

219
            close(pipefd[1]);
220

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

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

232
            waitpid(pid, NULL, 0);
233

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


/**
 * @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);

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

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

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

    /* 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;
}
295

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

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

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


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

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

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

351
#if TEST_OOM
352 353 354 355 356 357 358 359 360 361
    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;
    }

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

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

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

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

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

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

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

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

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

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

432 433 434 435 436 437 438 439 440 441 442 443 444 445
        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);
            }
        }

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

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

    virResetLastError();
    return ret;
461
}
462 463


464
#ifdef HAVE_REGEX_H
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 507
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;
}
508 509 510 511 512 513 514
#else
int virtTestClearLineRegex(const char *pattern ATTRIBUTE_UNUSED,
                           char *str ATTRIBUTE_UNUSED)
{
    return 0;
}
#endif