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.

asked Feb 18, 2024 at 7:51
\$\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.

answered Feb 18, 2024 at 8:40
\$\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\$ Commented Feb 19, 2024 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, 2024 at 13:13

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.