testutils.c 19.1 KB
Newer Older
K
Karel Zak 已提交
1
/*
2
 * testutils.c: basic test utils
K
Karel Zak 已提交
3
 *
4
 * Copyright (C) 2005-2011 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
#include "buf.h"
#include "logging.h"
36 37

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

41
#ifdef HAVE_PATHS_H
42
# include <paths.h>
43 44
#endif

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

E
Eric Blake 已提交
50
#include "virfile.h"
51

52
static unsigned int testDebug = -1;
53
static unsigned int testVerbose = -1;
54

55 56 57
static unsigned int testOOM = 0;
static unsigned int testCounter = 0;

E
Eric Blake 已提交
58 59 60
char *progname;
char *abs_srcdir;

K
Karel Zak 已提交
61 62 63
double
virtTestCountAverage(double *items, int nitems)
{
64 65
    long double sum = 0;
    int i;
K
Karel Zak 已提交
66

67 68
    for (i=1; i < nitems; i++)
        sum += items[i];
K
Karel Zak 已提交
69

70
    return (double) (sum / nitems);
K
Karel Zak 已提交
71 72
}

73
ATTRIBUTE_FMT_PRINTF(3,4)
74 75 76 77 78
void virtTestResult(const char *name, int ret, const char *msg, ...)
{
    va_list vargs;
    va_start(vargs, msg);

79 80 81
    if (testCounter == 0 && !virTestGetVerbose())
        fprintf(stderr, "      ");

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
    testCounter++;
    if (virTestGetVerbose()) {
        fprintf(stderr, "%3d) %-60s ", testCounter, name);
        if (ret == 0)
            fprintf(stderr, "OK\n");
        else {
            fprintf(stderr, "FAILED\n");
            if (msg) {
                vfprintf(stderr, msg, vargs);
            }
        }
    } else {
        if (testCounter != 1 &&
            !((testCounter-1) % 40)) {
            fprintf(stderr, " %-3d\n", (testCounter-1));
            fprintf(stderr, "      ");
        }
        if (ret == 0)
            fprintf(stderr, ".");
        else
            fprintf(stderr, "!");
    }

    va_end(vargs);
}

108
/*
K
Karel Zak 已提交
109
 * Runs test and count average time (if the nloops is grater than 1)
110 111
 *
 * returns: -1 = error, 0 = success
K
Karel Zak 已提交
112 113
 */
int
114
virtTestRun(const char *title, int nloops, int (*body)(const void *data), const void *data)
K
Karel Zak 已提交
115
{
116 117
    int i, ret = 0;
    double *ts = NULL;
118

119 120 121
    if (testCounter == 0 && !virTestGetVerbose())
        fprintf(stderr, "      ");

122
    testCounter++;
123

124
    if (testOOM < 2) {
125 126
        if (virTestGetVerbose())
            fprintf(stderr, "%2d) %-65s ... ", testCounter, title);
127
    }
128 129 130 131 132 133 134 135 136 137

    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);
138

139
        virResetLastError();
140
        ret = body(data);
141
        virErrorPtr err = virGetLastError();
142 143 144 145 146 147 148 149 150 151
        if (err) {
            if (virTestGetVerbose() || virTestGetDebug())
                virDispatchError(NULL);
        }

        if (ret != 0) {
            break;
        }

        if (ts) {
152 153 154 155
            GETTIMEOFDAY(&after);
            ts[i] = DIFF_MSEC(&after, &before);
        }
    }
156
    if (testOOM < 2) {
157 158 159 160 161 162
        if (virTestGetVerbose()) {
            if (ret == 0 && ts)
                fprintf(stderr, "OK     [%.5f ms]\n",
                        virtTestCountAverage(ts, nloops));
            else if (ret == 0)
                fprintf(stderr, "OK\n");
163 164
            else if (ret == EXIT_AM_SKIP)
                fprintf(stderr, "SKIP\n");
165 166 167 168 169 170 171 172 173 174
            else
                fprintf(stderr, "FAILED\n");
        } else {
            if (testCounter != 1 &&
                !((testCounter-1) % 40)) {
                fprintf(stderr, " %-3d\n", (testCounter-1));
                fprintf(stderr, "      ");
            }
            if (ret == 0)
                fprintf(stderr, ".");
175 176
            else if (ret == EXIT_AM_SKIP)
                fprintf(stderr, "_");
177 178 179
            else
                fprintf(stderr, "!");
        }
180
    }
181

182
    free(ts);
183
    return ret;
K
Karel Zak 已提交
184
}
185

186 187 188 189 190 191
/* Allocate BUF to the size of FILE. Read FILE into buffer BUF.
   Upon any failure, diagnose it and return -1, but don't bother trying
   to preserve errno. Otherwise, return the number of bytes copied into BUF. */
int
virtTestLoadFile(const char *file, char **buf)
{
192
    FILE *fp = fopen(file, "r");
193
    struct stat st;
194 195
    char *tmp;
    int len, tmplen, buflen;
196

197 198
    if (!fp) {
        fprintf (stderr, "%s: failed to open: %s\n", file, strerror(errno));
199
        return -1;
200
    }
201 202

    if (fstat(fileno(fp), &st) < 0) {
203
        fprintf (stderr, "%s: failed to fstat: %s\n", file, strerror(errno));
204
        VIR_FORCE_FCLOSE(fp);
205 206 207
        return -1;
    }

208 209 210 211
    tmplen = buflen = st.st_size + 1;

    if (VIR_ALLOC_N(*buf, buflen) < 0) {
        fprintf (stderr, "%s: larger than available memory (> %d)\n", file, buflen);
212
        VIR_FORCE_FCLOSE(fp);
213 214 215
        return -1;
    }

216
    tmp = *buf;
217
    (*buf)[0] = '\0';
218
    if (st.st_size) {
219 220 221
        /* read the file line by line */
        while (fgets(tmp, tmplen, fp) != NULL) {
            len = strlen(tmp);
222 223 224
            /* stop on an empty line */
            if (len == 0)
                break;
225 226 227 228 229 230 231 232 233 234
            /* remove trailing backslash-newline pair */
            if (len >= 2 && tmp[len-2] == '\\' && tmp[len-1] == '\n') {
                len -= 2;
                tmp[len] = '\0';
            }
            /* advance the temporary buffer pointer */
            tmp += len;
            tmplen -= len;
        }
        if (ferror(fp)) {
235
            fprintf (stderr, "%s: read failed: %s\n", file, strerror(errno));
236
            VIR_FORCE_FCLOSE(fp);
237
            free(*buf);
238 239
            return -1;
        }
240 241
    }

242
    VIR_FORCE_FCLOSE(fp);
243
    return strlen(*buf);
244 245
}

A
Atsushi SAKAI 已提交
246
#ifndef WIN32
247 248
static
void virtTestCaptureProgramExecChild(const char *const argv[],
249 250 251 252 253 254
                                     int pipefd) {
    int i;
    int open_max;
    int stdinfd = -1;
    const char *const env[] = {
        "LANG=C",
255
# if WITH_DRIVER_MODULES
256
        "LIBVIRT_DRIVER_DIR=" TEST_DRIVER_DIR,
257
# endif
258 259 260
        NULL
    };

261
    if ((stdinfd = open("/dev/null", O_RDONLY)) < 0)
262 263 264 265 266
        goto cleanup;

    open_max = sysconf (_SC_OPEN_MAX);
    for (i = 0; i < open_max; i++) {
        if (i != stdinfd &&
267 268 269 270
            i != pipefd) {
            int tmpfd = i;
            VIR_FORCE_CLOSE(tmpfd);
        }
271 272 273 274 275 276
    }

    if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
        goto cleanup;
    if (dup2(pipefd, STDOUT_FILENO) != STDOUT_FILENO)
        goto cleanup;
277
    if (dup2(pipefd, STDERR_FILENO) != STDERR_FILENO)
278 279 280 281
        goto cleanup;

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

 cleanup:
284
    VIR_FORCE_CLOSE(stdinfd);
285 286
}

287 288 289
int
virtTestCaptureProgramOutput(const char *const argv[], char **buf, int maxlen)
{
290
    int pipefd[2];
291
    int len;
292 293 294 295 296 297

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

    int pid = fork();
    switch (pid) {
298
    case 0:
299
        VIR_FORCE_CLOSE(pipefd[0]);
300 301
        virtTestCaptureProgramExecChild(argv, pipefd[1]);

302
        VIR_FORCE_CLOSE(pipefd[1]);
303 304
        _exit(1);

305 306
    case -1:
        return -1;
307

308
    default:
309 310 311 312
        VIR_FORCE_CLOSE(pipefd[1]);
        len = virFileReadLimFD(pipefd[0], maxlen, buf);
        VIR_FORCE_CLOSE(pipefd[0]);
        waitpid(pid, NULL, 0);
313

314
        return len;
315
    }
316
}
317
#else /* !WIN32 */
318 319 320 321 322
int
virtTestCaptureProgramOutput(const char *const argv[] ATTRIBUTE_UNUSED,
                             char **buf ATTRIBUTE_UNUSED,
                             int maxlen ATTRIBUTE_UNUSED)
{
323 324
    return -1;
}
A
Atsushi SAKAI 已提交
325
#endif /* !WIN32 */
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344


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

345
    if (!virTestGetDebug())
346 347
        return 0;

348
    if (virTestGetDebug() < 2) {
349 350 351 352 353 354
        /* Skip to first character where they differ */
        while (*expectStart && *actualStart &&
               *actualStart == *expectStart) {
            actualStart++;
            expectStart++;
        }
355

356 357 358 359 360 361 362
        /* Work backwards to last character where they differ */
        while (actualEnd > actualStart &&
               expectEnd > expectStart &&
               *actualEnd == *expectEnd) {
            actualEnd--;
            expectEnd--;
        }
363 364 365
    }

    /* Show the trimmed differences */
E
Eric Blake 已提交
366
    fprintf(stream, "\nOffset %d\nExpect [", (int) (expectStart - expect));
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
    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;
}
382

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
/**
 * @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 virtTestDifferenceBin(FILE *stream,
                          const char *expect,
                          const char *actual,
                          size_t length)
{
    size_t start = 0, end = length;
    ssize_t i;

    if (!virTestGetDebug())
        return 0;

    if (virTestGetDebug() < 2) {
        /* Skip to first character where they differ */
        for (i = 0 ; i < length ; i++) {
            if (expect[i] != actual[i]) {
                start = i;
                break;
            }
        }

        /* Work backwards to last character where they differ */
        for (i = (length -1) ; i >= 0 ; i--) {
            if (expect[i] != actual[i]) {
                end = i;
                break;
            }
        }
    }
E
Eric Blake 已提交
419
    /* Round to nearest boundary of 4, except that last word can be short */
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
    start -= (start % 4);
    end += 4 - (end % 4);
    if (end >= length)
        end = length - 1;

    /* Show the trimmed differences */
    fprintf(stream, "\nExpect [ Region %d-%d", (int)start, (int)end);
    for (i = start; i < end ; i++) {
        if ((i % 4) == 0)
            fprintf(stream, "\n    ");
        fprintf(stream, "0x%02x, ", ((int)expect[i])&0xff);
    }
    fprintf(stream, "]\n");
    fprintf(stream, "Actual [ Region %d-%d", (int)start, (int)end);
    for (i = start; i < end ; i++) {
        if ((i % 4) == 0)
            fprintf(stream, "\n    ");
        fprintf(stream, "0x%02x, ", ((int)actual[i])&0xff);
    }
    fprintf(stream, "]\n");

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

    return 0;
}

447
#if TEST_OOM
448 449 450 451
static void
virtTestErrorFuncQuiet(void *data ATTRIBUTE_UNUSED,
                       virErrorPtr err ATTRIBUTE_UNUSED)
{ }
452
#endif
453

454 455 456 457 458 459 460 461 462 463 464
struct virtTestLogData {
    virBuffer buf;
};

static struct virtTestLogData testLog = { VIR_BUFFER_INITIALIZER };

static int
virtTestLogOutput(const char *category ATTRIBUTE_UNUSED,
                  int priority ATTRIBUTE_UNUSED,
                  const char *funcname ATTRIBUTE_UNUSED,
                  long long lineno ATTRIBUTE_UNUSED,
465 466 467
                  const char *timestamp,
                  const char *str,
                  void *data)
468 469
{
    struct virtTestLogData *log = data;
470 471
    virBufferAsprintf(&log->buf, "%s: %s", timestamp, str);
    return strlen(timestamp) + 2 + strlen(str);
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
}

static void
virtTestLogClose(void *data)
{
    struct virtTestLogData *log = data;

    virBufferFreeAndReset(&log->buf);
}

/* Return a malloc'd string (possibly with strlen of 0) of all data
 * logged since the last call to this function, or NULL on failure.  */
char *
virtTestLogContentAndReset(void)
{
    char *ret;

    if (virBufferError(&testLog.buf))
        return NULL;
    ret = virBufferContentAndReset(&testLog.buf);
    return ret ? ret : strdup("");
}

495
#if TEST_OOM_TRACE
496
static void
497
virtTestErrorHook(int n, void *data ATTRIBUTE_UNUSED)
498 499 500 501 502 503 504 505 506
{
    void *trace[30];
    int ntrace = ARRAY_CARDINALITY(trace);
    int i;
    char **symbols = NULL;

    ntrace = backtrace(trace, ntrace);
    symbols = backtrace_symbols(trace, ntrace);
    if (symbols) {
507
        fprintf(stderr, "Failing allocation %d at:\n", n);
508 509 510 511 512 513 514
        for (i = 0 ; i < ntrace ; i++) {
            if (symbols[i])
                fprintf(stderr, "  TRACE:  %s\n", symbols[i]);
        }
        free(symbols);
    }
}
515
#endif
516

517 518 519 520
static unsigned int
virTestGetFlag(const char *name) {
    char *flagStr;
    unsigned int flag;
521

522
    if ((flagStr = getenv(name)) == NULL)
523 524
        return 0;

525
    if (virStrToLong_ui(flagStr, NULL, 10, &flag) < 0)
526 527
        return 0;

528 529 530 531
    return flag;
}

unsigned int
532
virTestGetDebug(void) {
533 534
    if (testDebug == -1)
        testDebug = virTestGetFlag("VIR_TEST_DEBUG");
535 536
    return testDebug;
}
537

538
unsigned int
539
virTestGetVerbose(void) {
540 541 542 543 544
    if (testVerbose == -1)
        testVerbose = virTestGetFlag("VIR_TEST_VERBOSE");
    return testVerbose || virTestGetDebug();
}

545 546
int virtTestMain(int argc,
                 char **argv,
E
Eric Blake 已提交
547
                 int (*func)(void))
548 549
{
    int ret;
550
    bool abs_srcdir_cleanup = false;
551
#if TEST_OOM
552 553
    int approxAlloc = 0;
    int n;
554
    char *oomStr = NULL;
555
    int oomCount;
556 557 558
    int mp = 0;
    pid_t *workers;
    int worker = 0;
559 560
#endif

E
Eric Blake 已提交
561
    abs_srcdir = getenv("abs_srcdir");
562 563 564 565
    if (!abs_srcdir) {
        abs_srcdir = getcwd(NULL, 0);
        abs_srcdir_cleanup = true;
    }
E
Eric Blake 已提交
566 567 568 569 570 571 572 573 574 575 576
    if (!abs_srcdir)
        exit(EXIT_AM_HARDFAIL);

    progname = argv[0];
    if (STRPREFIX(progname, "./"))
        progname += 2;
    if (argc > 1) {
        fprintf(stderr, "Usage: %s\n", argv[0]);
        return EXIT_FAILURE;
    }
    fprintf(stderr, "TEST: %s\n", progname);
577

578
    if (virThreadInitialize() < 0 ||
579 580
        virErrorInitialize() < 0 ||
        virRandomInitialize(time(NULL) ^ getpid()))
581 582
        return 1;

583
    virLogSetFromEnv();
584 585 586 587 588
    if (!getenv("LIBVIRT_DEBUG") && !virLogGetNbOutputs()) {
        if (virLogDefineOutput(virtTestLogOutput, virtTestLogClose, &testLog,
                               0, 0, NULL, 0) < 0)
            return 1;
    }
589

590
#if TEST_OOM
591 592 593 594 595 596 597 598 599 600
    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;
    }

601 602 603
    if (getenv("VIR_TEST_MP") != NULL) {
        mp = sysconf(_SC_NPROCESSORS_ONLN);
        fprintf(stderr, "Using %d worker processes\n", mp);
604 605 606 607
        if (VIR_ALLOC_N(workers, mp) < 0) {
            ret = EXIT_FAILURE;
            goto cleanup;
        }
608 609
    }

D
Daniel P. Berrange 已提交
610
    /* Run once to prime any static allocations & ensure it passes */
E
Eric Blake 已提交
611
    ret = (func)();
612
    if (ret != EXIT_SUCCESS)
613
        goto cleanup;
614

615
# if TEST_OOM_TRACE
616
    if (virTestGetDebug())
617
        virAllocTestHook(virtTestErrorHook, NULL);
618
# endif
619 620 621 622 623 624

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

D
Daniel P. Berrange 已提交
625 626 627
        virAllocTestInit();

        /* Run again to count allocs, and ensure it passes :-) */
E
Eric Blake 已提交
628
        ret = (func)();
D
Daniel P. Berrange 已提交
629 630 631
        if (ret != EXIT_SUCCESS)
            goto cleanup;

632 633
        approxAlloc = virAllocTestCount();
        testCounter++;
634
        if (virTestGetDebug())
635 636 637 638
            fprintf(stderr, "%d) OOM...\n", testCounter);
        else
            fprintf(stderr, "%d) OOM of %d allocs ", testCounter, approxAlloc);

639 640 641 642 643 644 645 646 647 648 649
        if (mp) {
            int i;
            for (i = 0 ; i < mp ; i++) {
                workers[i] = fork();
                if (workers[i] == 0) {
                    worker = i + 1;
                    break;
                }
            }
        }

650 651
        /* Run once for each alloc, failing a different one
           and validating that the test case failed */
652
        for (n = 0; n < approxAlloc && (!mp || worker) ; n++) {
653 654
            if (mp &&
                (n % mp) != (worker - 1))
655
                continue;
656
            if (!virTestGetDebug()) {
657 658 659 660
                if (mp)
                    fprintf(stderr, "%d", worker);
                else
                    fprintf(stderr, ".");
661 662 663 664
                fflush(stderr);
            }
            virAllocTestOOM(n+1, oomCount);

E
Eric Blake 已提交
665
            if (((func)()) != EXIT_FAILURE) {
666 667 668 669 670
                ret = EXIT_FAILURE;
                break;
            }
        }

671 672 673 674 675 676 677 678 679 680 681 682 683 684
        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);
            }
        }

685
        if (virTestGetDebug())
686 687 688 689 690 691 692
            fprintf(stderr, " ... OOM of %d allocs", approxAlloc);

        if (ret == EXIT_SUCCESS)
            fprintf(stderr, " OK\n");
        else
            fprintf(stderr, " FAILED\n");
    }
693
cleanup:
694
#else
E
Eric Blake 已提交
695
    ret = (func)();
696
#endif
697

698 699
    if (abs_srcdir_cleanup)
        VIR_FREE(abs_srcdir);
700
    virResetLastError();
701
    if (!virTestGetVerbose() && ret != EXIT_AM_SKIP) {
E
Eric Blake 已提交
702 703
        if (testCounter == 0 || testCounter % 40)
            fprintf(stderr, "%*s", 40 - (testCounter % 40), "");
704 705
        fprintf(stderr, " %-3d %s\n", testCounter, ret == 0 ? "OK" : "FAIL");
    }
706
    return ret;
707
}
708 709


710
#ifdef HAVE_REGEX_H
711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
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;
}
754 755 756 757 758 759 760
#else
int virtTestClearLineRegex(const char *pattern ATTRIBUTE_UNUSED,
                           char *str ATTRIBUTE_UNUSED)
{
    return 0;
}
#endif