4. Test Suite

A test suite is used to ensure that things function the way they are supposed to.

4.1. Top

<<funcdefs>>=
#ifdef ORGPARSE_TEST
int orgparse_test_suite(void);
#endif
<<test_program>>=
typedef struct {
    const char *name;
    int (*test)(void);
} test_entry;

enum {
    OK,
    FAIL,
    IGNORE
};

<<tests>>

test_entry Tests[] =
{
<<test_entries>>
};

int orgparse_test_suite(void)
{
    int n;
    int ntests;
    test_entry *e;
    int rc;
    int nerr;

    ntests = sizeof(Tests)/sizeof(*Tests);
    rc = 0;
    nerr = 0;

    for(n = 0; n < ntests; n++) {
        e = &Tests[n];
        rc = e->test();
        printf("[%d/%d] %s: ", n + 1, ntests, e->name);
        switch (rc) {
            case OK:
                printf("OK\n");
                break;
            case FAIL:
                nerr++;
                printf("FAIL\n");
                break;
            default:
                printf("???\n");
                break;
        }
    }

    if (nerr) {
        printf("\nTest suite failed with %d error(s)\n", nerr);
        rc = 1;
    } else {
        printf("\nTest suite successful\n");
        rc = 0;
    }
    return rc;
}

4.2. Tests

4.2.1. Template Test

Just to get things started. A boilerpalte test that returns "okay".

<<test_entries>>=
{"Template Test", test_foo},
<<tests>>=
static int test_foo(void)
{
    return OK;
}

4.2.2. Extra Newline at Ending Aux Block

This is a particular edge case that happens when an aux block ends up at the end of a text block. It will go and print an extra newline statement.

<<test_entries>>=
{"Extra newline at ending aux block", test_ending_auxblock},
<<tests>>=
static void ending_auxblock_newline(void *ud,
                                    const char *str,
                                    size_t sz)
{
    int *nl;
    nl = ud;
    *nl = (*nl) + 1;
}

static int test_ending_auxblock(void)
{
    orgparse op;
    int nl;
    int rc;

    nl = 0;
    orgparse_init(&op);
    orgparse_set_newline(&op, ending_auxblock_newline);
    orgparse_run(&op, "@! some text !@\n\none.", 23, &nl);
    rc = OK;
    if (nl != 0) {
        printf("%d newlines when it should have been 0\n",
               nl);
        rc = FAIL;
    }
    return rc;
}

4.2.3. Text Before Code Block

Text before a block causes a code block to glitchy by one.

<<test_entries>>=
{"Text before codeblock", test_text_before_blk},
<<tests>>=
typedef struct {
    const char *code;
    size_t sz;
    int found_codeblock;
    int off_by_one;
} tbb_d;

static void tbb_codeblock(void *ud,
                          const char *str,
                          size_t sz)
{
    tbb_d *tbb;
    tbb = ud;

    tbb->code = str;
    tbb->sz = sz;
    tbb->found_codeblock = 1;
}

static void tbb_text(void *ud,
                     const char *str,
                     size_t sz)
{
    tbb_d *tbb;
    tbb = ud;

    if (str[0] == '+') {
        tbb->off_by_one = 1;
    }
}

static int test_text_before_blk(void)
{
    orgparse op;
    tbb_d tbb;
    const char *str =
        "one.\n"
        "#+NAME: block\n"
        "#+BEGIN_SRC text\n"
        "this is a codeblock.\n"
        "#+END_SRC";
    int rc;

    tbb.sz = 0;
    tbb.code = NULL;
    tbb.found_codeblock = 0;
    tbb.off_by_one = 0;

    rc = OK;

    orgparse_init(&op);
    orgparse_set_codeblock(&op, tbb_codeblock);
    orgparse_set_text(&op, tbb_text);
    orgparse_run(&op, str, strlen(str), &tbb);

    if (!tbb.found_codeblock) {
        printf("Codeblock not found\n");
        rc = FAIL;
    }

    if (tbb.off_by_one) {
        printf("Codeblock parser is off by one (+BEGIN)\n");
        rc = FAIL;
    }

    return rc;
}

4.2.4. Invalid Paragraph Start

This happens at the end of a file with any non-text thing (such as a header). While that bug has been fixed, a new bug has occured where a header does not get parsed if there is no newline. Trying to get to the bottom of that as well now.

<<test_entries>>=
{"Invalid Paragraph Start", test_invalid_pgrph},
<<tests>>=
typedef struct {
    int found;
    int headers;
} ip_d;

static void ip_pgrph(void *ud, int mode)
{
    ip_d *ip;
    ip = ud;
    ip->found++;
}

static void ip_header(void *ud, const char *str, size_t sz, int lvl)
{
    ip_d *ip;
    ip = ud;
    ip->headers++;
}

static int test_invalid_pgrph(void)
{
    orgparse op;
    int rc;
    ip_d ip;
    const char *str = "* A\n* B";

    ip.found = 0;
    ip.headers = 0;

    rc = OK;

    orgparse_init(&op);
    orgparse_set_pgrph(&op, ip_pgrph);
    orgparse_set_header(&op, ip_header);
    orgparse_run(&op, str, strlen(str), &ip);

    if (ip.found) {
        printf("Found %d paragraph calls\n", ip.found);
        rc = FAIL;
    }

    if (ip.headers != 2) {
        printf("Found %d headers, expected 2\n",
               ip.headers);
    }

    return rc;
}

4.2.5. New paragraph block after header

For some reason, a new paragraph block will not start in a second header. This test will make sure it passes.

<<test_entries>>=
{"New Paragraph After Block Header", test_new_pgrph},
<<tests>>=
typedef struct {
    int found;
    int headers;
} np_d;

static void np_pgrph(void *ud, int mode)
{
    np_d *np;
    np = ud;
    if (mode == 0) np->found++;
}

static void np_header(void *ud, const char *str, size_t sz, int lvl)
{
    np_d *np;
    np = ud;
    np->headers++;
}

static int test_new_pgrph(void)
{
    orgparse op;
    int rc;
    np_d np;
    const char *str = "* A\n123\n* B\n456";

    np.found = 0;
    np.headers = 0;

    rc = OK;

    orgparse_init(&op);
    orgparse_set_pgrph(&op, np_pgrph);
    orgparse_set_header(&op, np_header);
    orgparse_run(&op, str, strlen(str), &np);

    if (np.found != 2) {
        printf("Found %d paragraph call(s)\n", np.found);
        printf("Expected 2\n");
        rc = FAIL;
    }

    if (np.headers != 2) {
        printf("Found %d headers, expected 2\n",
               np.headers);
    }

    return rc;
}

4.2.6. Starting new paragraphs

A new paragraph should happen when there is an empty line.

<<test_entries>>=
{"Starting new paragraphs", test_start_pgrph},
<<tests>>=
typedef struct {
    int n;
} sp_d;

static void sp_pgrph(void *ud, int mode)
{
    sp_d *sp;
    sp = ud;
    if (mode == 0) sp->n++;
}

static int test_start_pgrph(void)
{
    orgparse op;
    int rc;
    sp_d sp;
    const char *str = "abc\n\ndef\n\nghi";

    sp.n = 0;

    rc = OK;

    orgparse_init(&op);
    orgparse_set_pgrph(&op, sp_pgrph);
    orgparse_run(&op, str, strlen(str), &sp);

    if (sp.n != 3) {
        printf("Found %d paragraph start(s). ", sp.n);
        printf("Expected 3.\n");
        rc = FAIL;
    }

    return rc;
}

4.2.7. Multiple Aux Blocks.

Two aux blocks, separated by an empty line, currently causes the second auxblock to be parsed as text.

<<test_entries>>=
{"Multiple Aux Blocks", test_mult_auxblocks},
<<tests>>=
typedef struct {
    int na;
} ma_d;

static void na_aux(void *ud, const char *buf, size_t sz)
{
    ma_d *ma;
    ma = ud;
    ma->na++;
}

static int test_mult_auxblocks(void)
{
    orgparse op;
    int rc;
    ma_d ma;
    const char *str = "@!foo!@\n\n@!bar!@";

    ma.na = 0;

    rc = OK;

    orgparse_init(&op);
    orgparse_set_aux(&op, na_aux);
    orgparse_run(&op, str, strlen(str), &ma);

    if (ma.na != 2) {
        printf("Found %d aux blocks. ", ma.na);
        printf("Expected 2.\n");
        rc = FAIL;
    }

    return rc;
}

4.2.8. Start a new block within a block

This problem happens with the following weewiki text.

@!(org "foo")!@

@!(org "bar")!@

@!(org "cat")!@

This generates the following html:

<p>foo</p>
bar<br>
cat</p>

When it should be more like:

<p>foo</p>
<p>bar</p>
<p>car</p>

This test aims to reproduce the situations, and count the number of paragraph starts. It should be 3 starts, but it currently only returns 1.

<<test_entries>>=
{"Start block within a block", test_block_block},
<<tests>>=
typedef struct {
    int b;
    int e;
    int t;
    orgparse_state s;
    orgparse *op;
} bb_d;

static void bb_aux(void *ud, const char *buf, size_t sz)
{
    bb_d *bb;
    orgparse_state new;
    orgparse_state_flags *f;
    bb = ud;

    orgparse_state_init(&new, bb->op, buf, sz, bb);
    f = orgparse_state_flags_get(&bb->s);
    orgparse_state_flags_set(&new, f);
    orgparse_state_run(&new);
}

static void bb_pgrph(void *ud, int mode)
{
    bb_d *bb;
    bb = ud;
    if (mode == 0) bb->b++;
    else bb->e++;
}

static void bb_txt(void *ud, const char *buf, size_t sz)
{
    bb_d *bb;
    bb = ud;
    bb->t++;
}

static int test_block_block(void)
{
    orgparse op;
    int rc;
    bb_d bb;
    const char *str = "@!foo!@\n\n@!bar!@\n\n@!cat!@";

    bb.b = 0;
    bb.e = 0;
    bb.t = 0;

    rc = OK;

    orgparse_init(&op);
    bb.op = &op;
    orgparse_set_aux(&op, bb_aux);
    orgparse_set_pgrph(&op, bb_pgrph);
    orgparse_set_text(&op, bb_txt);
    orgparse_state_init(&bb.s, &op, str, strlen(str), &bb);
    orgparse_state_run(&bb.s);
    orgparse_end(&op, &bb, &bb.s);

    if (bb.b != 3) {
        printf("Found %d paragraphs begins. ", bb.b);
        printf("Expected 3.\n");
        rc = FAIL;
    }

    if (bb.e != 3) {
        printf("Found %d paragraphs ends. ", bb.e);
        printf("Expected 3.\n");
        rc = FAIL;
    }

    if (bb.t != 3) {
        printf("Found %d textblocks. ", bb.t);
        printf("Expected 3.\n");
        rc = FAIL;
    }

    return rc;
}

4.2.9. Wrap-up always has end paragraph

Things always end up in text mode, regardless of mode. This causes an end paragraph callback to happen.

<<test_entries>>=
{"Invalid end paragraph at wrapup", test_ep_at_wrapup},
<<tests>>=
typedef struct {
    int e;
    int b;
} epwu_d;

static void epwu_pgrph(void *ud, int mode)
{
    epwu_d *epwu;
    epwu = ud;
    if (mode == 0) epwu->b++;
    else epwu->e++;
}

static int test_ep_at_wrapup(void)
{
    orgparse op;
    int rc;
    epwu_d epwu;
    const char *str = "* Header\n";

    epwu.b = 0;
    epwu.e = 0;

    rc = OK;

    orgparse_init(&op);
    orgparse_set_pgrph(&op, epwu_pgrph);
    orgparse_run(&op, str, strlen(str), &epwu);

    if (epwu.e != 0) {
        printf("Found %d paragraphs ends. ", epwu.e);
        printf("Expected no paragraph ends.\n");
        rc = FAIL;
    }

    return rc;
}

4.2.10. Parsing Continuations

Parsing continuations refer to being able save state information between multiple parse calls, to the point where functionally, it is all one continuous parse call.

At the time of writing, this sort of functionality does not exactly exist, but it is needed if Janet is to parse org blocks.

Right now, the following code will make two paragraphs out of the words "foo" and "bar", because of the extra line break.

foo

bar

This should do the same thing in weewiki markup + Janet, but right now it doesn't. I think it is because state is not being saved. Only one paragraph block is being registered.

@!
(org "foo\n\n")
(org "bar\n")
!@

This test will simulate what is happening in the Janet code, and count the paragraph starts/ends.

<<test_entries>>=
{"Parsing Continuations", test_continuations},
<<tests>>=
typedef struct {
    int b;
    int e;
    orgparse_state s1;
    orgparse_state s2;
} cont_d;

static void cont_pgrph(void *ud, int mode)
{
    cont_d *cont;
    cont = ud;
    if (mode == 0) cont->b++;
    else cont->e++;
}

static int test_continuations(void)
{
    orgparse op;
    int rc;
    cont_d cont;
    orgparse_state_flags *f;
    const char *blk1 = "foo\n\n";
    const char *blk2 = "bar\n";

    cont.b = 0;
    cont.e = 0;

    rc = OK;

    orgparse_init(&op);

    orgparse_set_pgrph(&op, cont_pgrph);

    /* parse first block */

    orgparse_state_init(&cont.s1, &op, blk1, strlen(blk1), &cont);
    orgparse_state_run(&cont.s1);

    /* parse second block */
    orgparse_state_init(&cont.s2, &op, blk2, strlen(blk2), &cont);

    f = orgparse_state_flags_get(&cont.s1);

    if (f->newline == 0) {
        printf("Expected newline flag to be toggled on\n");
        rc = FAIL;
    }

    orgparse_state_flags_set(&cont.s2, f);
    orgparse_state_run(&cont.s2);


    orgparse_end(&op, &cont, &cont.s2);

    if (cont.b != 2) {
        printf("Found %d paragraphs begins. ", cont.b);
        printf("Expected 2.\n");
        rc = FAIL;
    }

    return rc;
}

4.2.11. Parse formatted text after newline

It seems my newline logic has added a new bug. So that's fun. Currently, any new paragraph that starts with formatted text or a link will get treated as plaintext.

This test will isolate the incident and try to do bold text on a newline. If no bold text is to be found, it is a bug.

<<test_entries>>=
{"Parse formatted text after newline", test_formnl},
<<tests>>=
typedef struct {
    int b;
} formnl_d;

static void formnl_bold(void *ud, const char *s, size_t sz)
{
    formnl_d *f;
    f = ud;
    f->b++;
}

static int test_formnl(void)
{
    orgparse op;
    formnl_d f;
    int rc;
    const char *txt = "text.\n\n*bold* text\n";

    rc = OK;
    orgparse_init(&op);
    f.b = 0;

    orgparse_set_bold(&op, formnl_bold);

    orgparse_run(&op, txt, strlen(txt), &f);

    if (f.b == 0) {
        printf("No bolds found\n");
        rc = FAIL;
    } else if (f.b != 1) {
        printf("Got %d bolds, expected only 1\n", f.b);
        rc = FAIL;
    }

    return rc;
}

4.2.12. Rogue Paragraph

This happens anytime there is a line break at the end of a file, like so:

Text.

Running orgparse_test run on this will produce the following:

PARAGRAPH BEGIN
text: 'Text.
'
PARAGRAPH END
PARAGRAPH END

I am guessing this has to do with the orgparse_wrapuplogic.

<<test_entries>>=
{"Rogue Paragraph End", test_rp},
<<tests>>=
typedef struct {
    int e;
} rp_d;

static void rp_pgrph(void *ud, int mode)
{
    rp_d *rp;
    rp = ud;
    if (mode) rp->e++;
}

static int test_rp(void)
{
    orgparse op;
    rp_d rp;
    int rc;
    orgparse_state state;
    const char *txt = "text.\n\n";

    rc = OK;
    orgparse_init(&op);
    rp.e = 0;

    orgparse_set_pgrph(&op, rp_pgrph);

    orgparse_init_and_run(&op,
                          txt,
                          strlen(txt),
                          &rp,
                          &state);
    orgparse_end(&op, &rp, &state);

    if (rp.e != 1) {
        printf("Wrong number of end paragraphs\n");
        printf("Expected 1, got %d\n", rp.e);
        if (rp.e == 2) {
            printf("Suspected rogue paragraph detected\n");
        }
        rc = FAIL;
    }

    return rc;
}

4.2.13. Newline after formatted text causes paragraph end

<<test_entries>>=
{"Paragraph after newlined formatted text", test_nlformpe},
<<tests>>=
typedef struct {
    int cnt;
} nlformpe_d;

static void nlformpe_pgrph(void *ud, int mode)
{
    nlformpe_d *x;
    x = ud;
    if (mode == 0) x->cnt++;
}

static int test_nlformpe(void)
{
    orgparse op;
    nlformpe_d x;
    int rc;
    const char *txt = "*foo*\nbar";

    rc = OK;
    orgparse_init(&op);
    x.cnt = 0;

    orgparse_set_pgrph(&op, nlformpe_pgrph);

    orgparse_run(&op, txt, strlen(txt), &x);

    if (x.cnt == 2) {
        printf("Incorrect number of paragarphs found\n");
        printf("Should only be 1.\n");
        printf("This is an expected bug.\n");
        rc = FAIL;
    } else if (x.cnt != 1) {
        printf("Unexpected number of paragraphs: %d",
               x.cnt);
    }

    return rc;
}

4.2.14. Paragraphs ending with link causes newline

This text:

[[link]]

[[link]]

Causes the following to happen

PARAGRAPH BEGIN
link_ref: 'link', 'link'
PARAGRAPH END
newline
PARAGRAPH BEGIN
link_ref: 'link', 'link'
PARAGRAPH END

The "newline" shouldn't be there. The test define below runs this text through the parser and sees if the newline callback gets called. If it does, the test has failed.

<<test_entries>>=
{"Paragraph ending with link causes newline", test_linknl},
<<tests>>=
static void linknl_nl(void *ud,
                      const char *dumb,
                      size_t dumbr)
{
    int *x;
    x = ud;
    *x = *x + 1; /* *x++ works too? */
}

static int test_linknl(void)
{
    orgparse op;
    int rc;
    int cnt;

    const char *txt = "[[link]]\n\n[[link]]";
    cnt = 0;

    rc = OK;
    orgparse_init(&op);

    orgparse_set_newline(&op, linknl_nl);

    orgparse_run(&op, txt, strlen(txt), &cnt);

    if (cnt != 0) {
        printf("Found a newline. Oops.\n");
        rc = FAIL;
    }

    return rc;
}

4.2.15. Formatted text does line break check

Formatted operations like this one should only exist on one line. The text parser should check for line breaks, and it doesn't right now.

At the moment, this code gets registered as a code block.

foo = one two three
bar
=
Does this work?

PARAGRAPH BEGIN
text: 'foo '
code: ' one two three
bar
'
text: '
Does this work?
'
PARAGRAPH END

In a similar vein, this is a valid tag, * this* is not. Also, *this * is not a valid tag. Since the solution for both of these is so similar, this test will measure for both.

<<test_entries>>=
{"Tag line break + space checker", test_linebreak},
<<tests>>=
static void linebreak_code(void *ud,
                           const char *str,
                           size_t sz)
{
    int *state;
    state = ud;
    *state |= 1;
}

static void linebreak_bold(void *ud,
                           const char *str,
                           size_t sz)
{
    int *state;
    state = ud;
    *state |= 2;
}

static int test_linebreak(void)
{
    orgparse op;
    int state;
    int rc;
    const char *txt =
        "foo = one two three\n"
        "bar\n"
        "=\n"
        "Does * this* *work *?";


    rc = OK;
    orgparse_init(&op);

    orgparse_set_code(&op, linebreak_code);
    orgparse_set_bold(&op, linebreak_bold);

    state = 0;
    orgparse_run(&op, txt, strlen(txt), &state);

    if (state & 1) {
        printf("Text should not have parsed as code.\n");
        rc = FAIL;
    }

    if (state & 2) {
        printf("Text should not have parsed as bold.\n");
        rc = FAIL;
    }

    return rc;
}

4.2.16. Optional newline after title

A TITLE line should be able include one optional newline without it actually being a newline.

<<test_entries>>=
{"Optional Newline after TITLE", test_onat},

When this happens:


#+TITLE: foo

Hello

The output looks like this:

title: 'foo'
PARAGRAPH BEGIN
PARAGRAPH END
PARAGRAPH BEGIN
text: 'Hello
'
PARAGRAPH END

It has an empty BEGIN/END paragraph, when that shouldn't be there.

Ideally, such a thing should look like this:

title: 'foo'
PARAGRAPH BEGIN
text: 'Hello
'
PARAGRAPH END

In other words, the title tag specifically is allowed to have on extra line immediately following it. I may make this true for all tags that start with "#+" later because it looks nice.

<<tests>>=
static void onat_pgrph(void *ud, int mode)
{
    if (!mode) {
        int *npgrph = ud;
        (*npgrph)++;
    }
}

static int test_onat(void)
{
    orgparse op;
    int rc;
    int npgrph;

    const char *txt =
        "#+TITLE: foo\n"
        "\n"
        "Hello.";

    npgrph = 0;

    rc = FAIL;

    orgparse_init(&op);
    orgparse_set_pgrph(&op, onat_pgrph);
    orgparse_run(&op, txt, strlen(txt), &npgrph);

    if (npgrph == 1) rc = OK;

    if (npgrph == 2) {
        fprintf(stderr, "2 paragraphs: title didn't slurp newline\n");
        rc = FAIL;
    }

    if (npgrph == 0) {
        fprintf(stderr, "0 paragraphs: is this thing working?\n");
        rc = FAIL;
    }

    return rc;
}

4.2.17. Bold text before newline does not end paragraph

The following text correctly produces two paragraphs.

*a*.

b

PARAGRAPH BEGIN
bold: 'a'
text: '.
'
PARAGRAPH END
PARAGRAPH BEGIN
text: 'b
'
PARAGRAPH END

Remove the '.', and only one paragraph is created.

*a*

b

PARAGRAPH BEGIN
bold: 'a'
text: '

'
text: 'b
'
PARAGRAPH END

The line break is incorrectly interpretted as just text, not a paragraph end.

<<test_entries>>=
{"bold text right before newline", test_boldtextnl},
<<tests>>=
static void boldtext_pbegin(void *ud, int mode)
{
    int *count;
    count = ud;
    if (!mode) *count = *count + 1;
}

static int test_boldtextnl(void)
{
    orgparse op;
    int count;
    int rc;
    const char *txt = "*a*\n\nb\n";

    rc = OK;
    orgparse_init(&op);

    orgparse_set_pgrph(&op, boldtext_pbegin);

    count = 0;

    orgparse_run(&op, txt, strlen(txt), &count);

    if (count != 2) {
        printf("Expected 2 paragraphs, got %d\n", count);
        rc = FAIL;
    }

    return rc;
}



prev | home | next