Skip to main content
system() is never portable
Source Link
Toby Speight
  • 91.6k
  • 14
  • 105
  • 333

It's good that we have a test program that allows us to exercise the code (although I would drop the non-portable and unnecessary system("clear")).

It's good that we have a test program that allows us to exercise the code.

It's good that we have a test program that allows us to exercise the code (although I would drop the non-portable and unnecessary system("clear")).

Test multi-char separators
Source Link
Toby Speight
  • 91.6k
  • 14
  • 105
  • 333
  • RepeatabilityRepeatability - while we are implementing code, we can repeatedly run one test until it gives the correct results.
  • ConfidenceAnalysability - weit's easy to run all the tests every timetest suite under Valgrind to ensure that we're not accessing memory we make a changeshouldn't, alerting us immediately if our new code broke something that used to workor leaking or double-freeing.
  • AnalysabilityReviewability - it's easy to run the test suite under Valgrind to ensure that we're not accessing memory we shouldn't, or leaking or doubleeveryone can see exactly what testing has been done -freeing and, crucially, whether something was missed.
  • Reviewability - everyone can see exactly what testing has been doneMantainability - and, cruciallywe run all the tests every time we make a change, whetheralerting us immediately if our new code broke something was missedthat used to work. Remember that the person modifying the code in future won't have the same insight into its operation as you do right now.
int main(void)
{
    unsigned failures = 0;
    failures += TEST_STR_SPLIT(NULL, NULL, 0, NULL);
    failures += TEST_STR_SPLIT(NULL, "abc", 1, NULL);
    failures += TEST_STR_SPLIT(NULL, "abc", -1, NULL);
    {
        const char *expected[] = {"abc", NULL};
        failures += TEST_STR_SPLIT("abc", NULL, 0, expected);
    }
    {
        const char *expected[] = {"abc", NULL};
        /* I would prefer this to return {"a", "b", "c", NULL }. */
        /* But the documentation is clear that's not what we get. */
        failures += TEST_STR_SPLIT("abc", "", 0, expected);
    }
    {
        /* I would prefer this to return {"", NULL }. */
        /* But the documentation is clear that's not what we get. */
        failures += TEST_STR_SPLIT("", ".", 0, NULL);
    }
    /* single character separator */
    {
        const char *expected[] = {"", NULL };
        failures += TEST_STR_SPLIT("", ".", -1, expected);
    }
    {
        const char *expected[] = {"=abc", NULL};
        failures += TEST_STR_SPLIT("=abc", "=", 0, expected);
    }
    {
        const char *expected[] = {"", "abc", NULL};
        failures += TEST_STR_SPLIT("=abc", "=", -1, expected);
    }
    {
        const char *expected[] = {"abc", "", NULL};
        failures += TEST_STR_SPLIT("abc=", "=", -1, expected);
    }
    {
        const char *expected[] = {"", "abc", "", NULL};
        failures += TEST_STR_SPLIT("=abc=", "=", 2-1, expected);
    }
    {
        const char *expected[] = {"ab", "cd", NULL};
        failures += TEST_STR_SPLIT("ab=cd", "=", -1, expected);
    }
    {
        const char *expected[] = {"ab", "cd", "ef", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef", "=", -1, expected);
    }
    {
        const char *expected[] = {"ab", "", "cd", NULL};
        failures += TEST_STR_SPLIT("ab==cd", "=", 2-1, expected);
    }
    {
        const char *expected[] = {"ab", "cd=ef", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef", "=", 1, expected);
    }
    {
        const char *expected[] = {"ab", "cd", "ef", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef", "=", 2, expected);
    }
    /* multi-char separator */
    {
        const char *expected[] = {"ab", "cd", "ef"NULL};
        failures += TEST_STR_SPLIT("ab==cd", "==", -1, expected);
    }
    {
        const char *expected[] = {"", "ab", "=cd", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef""==ab===cd", "=""==", 5-1, expected);
    }
    {
        const char *expected[] = {"ab", "cd", NULL};
        failures += TEST_STR_SPLIT("ab==>cd", "==>", -1, expected);
    }
    {
        const char *expected[] = {"ab", "-=cd", NULL};
        failures += TEST_STR_SPLIT("ab=-=-=cd", "=-=", -1, expected);
    }
    printf("There were %u test failures\n", failures);
    return failures > 0;
}
  • Repeatability - while we are implementing code, we can repeatedly run one test until it gives the correct results.
  • Confidence - we run all the tests every time we make a change, alerting us immediately if our new code broke something that used to work.
  • Analysability - it's easy to run the test suite under Valgrind to ensure that we're not accessing memory we shouldn't, or leaking or double-freeing.
  • Reviewability - everyone can see exactly what testing has been done - and, crucially, whether something was missed.
int main(void)
{
    unsigned failures = 0;
    failures += TEST_STR_SPLIT(NULL, NULL, 0, NULL);
    failures += TEST_STR_SPLIT(NULL, "abc", 1, NULL);
    failures += TEST_STR_SPLIT(NULL, "abc", -1, NULL);
    {
        const char *expected[] = {"abc", NULL};
        failures += TEST_STR_SPLIT("abc", NULL, 0, expected);
    }
    {
        const char *expected[] = {"abc", NULL};
        /* I would prefer this to return {"a", "b", "c", NULL }. */
        /* But the documentation is clear that's not what we get. */
        failures += TEST_STR_SPLIT("abc", "", 0, expected);
    }
    {
        /* I would prefer this to return {"", NULL }. */
        /* But the documentation is clear that's not what we get. */
        failures += TEST_STR_SPLIT("", ".", 0, NULL);
    }
    {
        const char *expected[] = {"=abc", NULL};
        failures += TEST_STR_SPLIT("=abc", "=", 0, expected);
    }
    {
        const char *expected[] = {"", "abc", NULL};
        failures += TEST_STR_SPLIT("=abc", "=", 1, expected);
    }
    {
        const char *expected[] = {"abc", "", NULL};
        failures += TEST_STR_SPLIT("abc=", "=", 1, expected);
    }
    {
        const char *expected[] = {"", "abc", "", NULL};
        failures += TEST_STR_SPLIT("=abc=", "=", 2, expected);
    }
    {
        const char *expected[] = {"ab", "cd", NULL};
        failures += TEST_STR_SPLIT("ab=cd", "=", 1, expected);
    }
    {
        const char *expected[] = {"ab", "", "cd", NULL};
        failures += TEST_STR_SPLIT("ab==cd", "=", 2, expected);
    }
    {
        const char *expected[] = {"ab", "cd=ef", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef", "=", 1, expected);
    }
    {
        const char *expected[] = {"ab", "cd", "ef", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef", "=", 2, expected);
    }
    {
        const char *expected[] = {"ab", "cd", "ef", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef", "=", 5, expected);
    }
    printf("There were %u test failures\n", failures);
    return failures > 0;
}
  • Repeatability - while we are implementing code, we can repeatedly run one test until it gives the correct results.
  • Analysability - it's easy to run the test suite under Valgrind to ensure that we're not accessing memory we shouldn't, or leaking or double-freeing.
  • Reviewability - everyone can see exactly what testing has been done - and, crucially, whether something was missed.
  • Mantainability - we run all the tests every time we make a change, alerting us immediately if our new code broke something that used to work. Remember that the person modifying the code in future won't have the same insight into its operation as you do right now.
int main(void)
{
    unsigned failures = 0;
    failures += TEST_STR_SPLIT(NULL, NULL, 0, NULL);
    failures += TEST_STR_SPLIT(NULL, "abc", 1, NULL);
    failures += TEST_STR_SPLIT(NULL, "abc", -1, NULL);
    {
        const char *expected[] = {"abc", NULL};
        failures += TEST_STR_SPLIT("abc", NULL, 0, expected);
    }
    {
        const char *expected[] = {"abc", NULL};
        /* I would prefer this to return {"a", "b", "c", NULL }. */
        /* But the documentation is clear that's not what we get. */
        failures += TEST_STR_SPLIT("abc", "", 0, expected);
    }
    {
        /* I would prefer this to return {"", NULL }. */
        /* But the documentation is clear that's not what we get. */
        failures += TEST_STR_SPLIT("", ".", 0, NULL);
    }
    /* single character separator */
    {
        const char *expected[] = {"", NULL };
        failures += TEST_STR_SPLIT("", ".", -1, expected);
    }
    {
        const char *expected[] = {"=abc", NULL};
        failures += TEST_STR_SPLIT("=abc", "=", 0, expected);
    }
    {
        const char *expected[] = {"", "abc", NULL};
        failures += TEST_STR_SPLIT("=abc", "=", -1, expected);
    }
    {
        const char *expected[] = {"abc", "", NULL};
        failures += TEST_STR_SPLIT("abc=", "=", -1, expected);
    }
    {
        const char *expected[] = {"", "abc", "", NULL};
        failures += TEST_STR_SPLIT("=abc=", "=", -1, expected);
    }
    {
        const char *expected[] = {"ab", "cd", NULL};
        failures += TEST_STR_SPLIT("ab=cd", "=", -1, expected);
    }
    {
        const char *expected[] = {"ab", "cd", "ef", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef", "=", -1, expected);
    }
    {
        const char *expected[] = {"ab", "", "cd", NULL};
        failures += TEST_STR_SPLIT("ab==cd", "=", -1, expected);
    }
    {
        const char *expected[] = {"ab", "cd=ef", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef", "=", 1, expected);
    }
    {
        const char *expected[] = {"ab", "cd", "ef", NULL};
        failures += TEST_STR_SPLIT("ab=cd=ef", "=", 2, expected);
    }
    /* multi-char separator */
    {
        const char *expected[] = {"ab", "cd", NULL};
        failures += TEST_STR_SPLIT("ab==cd", "==", -1, expected);
    }
    {
        const char *expected[] = {"", "ab", "=cd", NULL};
        failures += TEST_STR_SPLIT("==ab===cd", "==", -1, expected);
    }
    {
        const char *expected[] = {"ab", "cd", NULL};
        failures += TEST_STR_SPLIT("ab==>cd", "==>", -1, expected);
    }
    {
        const char *expected[] = {"ab", "-=cd", NULL};
        failures += TEST_STR_SPLIT("ab=-=-=cd", "=-=", -1, expected);
    }
    printf("There were %u test failures\n", failures);
    return failures > 0;
}
Bugfix
Source Link
Toby Speight
  • 91.6k
  • 14
  • 105
  • 333
static unsigned test_str_split(const char *file, int line,
                               const char *string, const char *delim, long max_splits,
                               const char **expected)
{
    char **const actual = str_split(string, delim, max_splits);
    if (!actual && !expected) {
        /* fine: expected and got NULL */
        return 0;
    }
    if (!actual) {
        goto error;
    }
    if (!expected) {
        goto error;
    }
    /* now check the elements */
    char **a = actual;
    const char **e = expected;
    while (*a && *expected*e) {
        if (!*a || !*expected*e || strcmp(*a++, *expected++*e++)) {
            goto error;
        }
    }

    free_strings_array(actual);
    return 0;

 error:
    printf("%s:%d: TEST FAILED: str_split(\"%s\", \"%s\", %ld)\n",
           file, line,
           string, delim, max_splits);
    printf("%s:%d: EXPECTED:\n", file, line);
    if (expected) {
        print_strings_array((char **)expected);
    } else {
        puts("(null)");
    }
    printf("%s:%d: ACTUAL:\n", file, line);
    if (actual) {
        print_strings_array(actual);
    } else {
        puts("(null)");
    }
    free_strings_array(actual);
    return 1;
}

#define TEST_STR_SPLIT(string, delim, max_splits, expected) \
    test_str_split(__FILE__, __LINE__, string, delim, max_splits, expected)
static unsigned test_str_split(const char *file, int line,
                               const char *string, const char *delim, long max_splits,
                               const char **expected)
{
    char **const actual = str_split(string, delim, max_splits);
    if (!actual && !expected) {
        /* fine: expected and got NULL */
        return 0;
    }
    if (!actual) {
        goto error;
    }
    if (!expected) {
        goto error;
    }
    /* now check the elements */
    char **a = actual;
    while (*a && *expected) {
        if (!*a || !*expected || strcmp(*a++, *expected++)) {
            goto error;
        }
    }

    free_strings_array(actual);
    return 0;

 error:
    printf("%s:%d: TEST FAILED: str_split(\"%s\", \"%s\", %ld)\n",
           file, line,
           string, delim, max_splits);
    printf("%s:%d: EXPECTED:\n", file, line);
    if (expected) {
        print_strings_array((char **)expected);
    } else {
        puts("(null)");
    }
    printf("%s:%d: ACTUAL:\n", file, line);
    if (actual) {
        print_strings_array(actual);
    } else {
        puts("(null)");
    }
    free_strings_array(actual);
    return 1;
}

#define TEST_STR_SPLIT(string, delim, max_splits, expected) \
    test_str_split(__FILE__, __LINE__, string, delim, max_splits, expected)
static unsigned test_str_split(const char *file, int line,
                               const char *string, const char *delim, long max_splits,
                               const char **expected)
{
    char **const actual = str_split(string, delim, max_splits);
    if (!actual && !expected) {
        /* fine: expected and got NULL */
        return 0;
    }
    if (!actual) {
        goto error;
    }
    if (!expected) {
        goto error;
    }
    /* now check the elements */
    char **a = actual;
    const char **e = expected;
    while (*a && *e) {
        if (!*a || !*e || strcmp(*a++, *e++)) {
            goto error;
        }
    }

    free_strings_array(actual);
    return 0;

 error:
    printf("%s:%d: TEST FAILED: str_split(\"%s\", \"%s\", %ld)\n",
           file, line,
           string, delim, max_splits);
    printf("%s:%d: EXPECTED:\n", file, line);
    if (expected) {
        print_strings_array((char **)expected);
    } else {
        puts("(null)");
    }
    printf("%s:%d: ACTUAL:\n", file, line);
    if (actual) {
        print_strings_array(actual);
    } else {
        puts("(null)");
    }
    free_strings_array(actual);
    return 1;
}

#define TEST_STR_SPLIT(string, delim, max_splits, expected) \
    test_str_split(__FILE__, __LINE__, string, delim, max_splits, expected)
added 217 characters in body
Source Link
Toby Speight
  • 91.6k
  • 14
  • 105
  • 333
Loading
Source Link
Toby Speight
  • 91.6k
  • 14
  • 105
  • 333
Loading