7
\$\begingroup\$

(This post is followed up by this one.)

I have rolled this simple unit test library for C:

com_github_coderodde_my_assert.h:

#ifndef COM_GITHUB_CODERODDE_MY_ASSERT_H
#define COM_GITHUB_CODERODDE_MY_ASSERT_H

#include <stdbool.h>
#include <stdio.h>

extern size_t passes;
extern size_t failed;

void print_test_statistics();

#define ASSERT_IMPL(COND, LINE) assert_impl(COND, #COND, __LINE__);
#define ASSERT(COND) ASSERT_IMPL(COND, __LINE__)

#endif

com_github_coderodde_my_assert.c:

#include "com_github_coderodde_my_assert.h"
#include <stdlib.h>

static size_t passes = 0;
static size_t failed = 0;

void assert_impl(bool passed,
                        const char* condition_text,
                        size_t line_number) {
    if (passed) {
        passes++;
    } else {
        failed++;

        printf("Failed condition \"%s\" at line %zu.\n",
            condition_text,
            line_number);
    }
}

static void print_bar(size_t passes, size_t failed) {
    size_t total_assertions = passes + failed;
    
    char bar[81];

    bar[0] = '[';
    bar[79] = ']';
    bar[80] = '\0';

    float ratio = (float)(passes) / (float)(total_assertions);

    size_t hash_symbols = (size_t)(78 * ratio);

    for (size_t i = 0; i < hash_symbols; i++) {
        bar[i + 1] = '#';
    }

    for (size_t i = 0; i < 78 - hash_symbols; i++) {
        bar[i + 1 + hash_symbols] = ' ';
    }

    if (failed != 0) {
        printf("\033[0;31m");
        printf("%s: %.2f%%.\n", bar, 100.0f * ratio);
    } else {
        printf("\033[0;32m");
        printf("%s: %.2F%%.\n", bar, 100.0f * ratio);
    }

    printf("\033[0m");
}

void print_test_statistics() {
    printf("Passes: %zu, failures: %zu, total assertions: %zu.\n",
           passes,
           failed,
           passes + failed);

    print_bar(passes, failed);
}

main.c:

#include "com_github_coderodde_my_assert.h"
#include <stdbool.h>

int main() {
    ASSERT(true);
    ASSERT(false);
    ASSERT(1 + 1 == 3);

    print_test_statistics();

    return 0;
}

Output

Critique request

As always, I would love to hear any comments on how to improve my result.

\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

The header doesn't need to include anything except <stddef.h> (to define size_t).


These two externs are never used:

extern size_t passes;
extern size_t failed;

We should just delete them - or perhaps change the shadowing declarations in the implementation file to refer to these globals instead.


Test output should go to stderr rather than being mixed in with program results. Consider printing the file name as well as line number, so the usual tools can take users straight to the error location.


Please use a suitable variable for this size (or derive the related values using sizeof bar):

char bar[81];

As it stands, there's too many places that need to be consistently modified if this value is adjusted.


We have a string here that assumes a specific output device:

    printf("\033[0;31m");

That looks a mess on other terminal types, and hampers parsing when output is not a terminal.


The main() always returns a success status, even when one or more tests fail. That's wrong, and makes it hard to use this test suite as part of a larger build process.

\$\endgroup\$
2
  • \$\begingroup\$ I'm not OP, but could you elaborate on how you would do the coloring instead? What's a good way to display color in your terminal, but not screw up other outputs? \$\endgroup\$
    – Opifex
    Commented Feb 19 at 13:08
  • \$\begingroup\$ @Opifex, you need to examine TERM environment variable and perhaps test isatty(). Or use a library such as Termcap or Curses that does those things for you. \$\endgroup\$ Commented Feb 19 at 13:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.