Simple Unit Test in CUnit test provider rolesUnnecessary unit test?Create better Unit testUnit test macroASP MVC Controller Unit TestUnit test with check testing frameworkAutoloader unit test - first attemptUnit test of argument parserC program implementing a simple test frameworkSimple unit test in C of input using freopen
What do I need to see before Spider-Man: Far From Home?
Sci-fi book (no magic, hyperspace jumps, blind protagonist)
Should I increase my 401(k) contributions, or increase my mortgage payments
What causes a fastener to lock?
How can I effectively map a multi-level dungeon?
Does 5e have an equivalent of the Psychic Paper from Doctor Who?
Would the Life cleric's Disciple of Life feature supercharge the Regenerate spell?
What is exact meaning of “ich wäre gern”?
Does the Milky Way orbit around anything?
Did William Shakespeare hide things in his writings?
Why did Super-VGA offer the 5:4 1280*1024 resolution?
Should I warn my boss I might take sick leave?
What is the fundamental difference between catching whales and hunting other animals?
Why weren't Gemini capsules given names?
How can select a specific triangle in my Delaunay mesh?
Is there a standard definition of the "stall" phenomena?
Convert integer to full text string duration
Is there a way to change the aspect ratio of a DNG file?
In the Seventh Seal why does Death let the chess game happen?
Park the computer
Are "confidant" and "confident" homophones?
The Purpose of "Natu"
What is the maximum amount of diamond in one Minecraft game?
Did Stalin kill all Soviet officers involved in the Winter War?
Simple Unit Test in C
Unit test provider rolesUnnecessary unit test?Create better Unit testUnit test macroASP MVC Controller Unit TestUnit test with check testing frameworkAutoloader unit test - first attemptUnit test of argument parserC program implementing a simple test frameworkSimple unit test in C of input using freopen
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
$begingroup$
Simple UnitTest macros utility in C. I decided to make this to be able to more easily organize my tests and I emphasized on readability. Please tell me what you think and if this utility would perform well in big unit tests.
timer.h
/* Simple timer macros utility */
#ifndef CMC_TIMER_H
#define CMC_TIMER_H
#include <time.h>
typedef struct timer_s
clock_t start;
clock_t stop;
double result;
timer_t;
#define TIMER_START(timer)
timer.start = clock();
#define TIMER_STOP(timer)
timer.stop = clock();
#define TIMER_CALC(timer)
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
#endif /* CMC_TIMER_H */
test.h
/**
* Simple Unit Test Utility
*
* CMC_CREATE_UNIT
* Create a UnitTest
* Parameters:
* - UNAME : UnitTest name (const char *)
* - VERBOSE : Print PASSED / FAILED messages if true (bool)
* - BODY : Body containing all tests related to this UnitTest
*
* CMC_CREATE_TEST
* Create a Test inside a UnitTest.
* Parameters:
* - TNAME : Test name (const char *)
* - BODY : Block of code containing a specific test
* Inside the BODY use the following macros:
* - CMC_TEST_PASS
* Pass current test.
* - CMC_TEST_FAIL
* Fail current test.
* - CMC_TEST_PASS_ELSE_FAIL
* If given expression is true, pass current test, else fail.
* - CMC_TEST_ABORT
* Abort current unit test and displays error message. All tests
* after the abort won't be called.
* All of these macros can only be called once per test.
*
* CMC_TEST_COLOR
* Define this macro to allow colored output.
*/
#ifndef CMC_TEST_H
#define CMC_TEST_H
#include "timer.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef CMC_TEST_COLOR
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
#endif
typedef struct cmc_test_info_s
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
bool aborted;
bool verbose;
cmc_test_info;
/* Logging function */
static void cmc_test_log(const char *unit_name, const char *current_test, bool aborted, bool passed)
if (aborted)
#ifdef CMC_TEST_COLOR
printf("UNIT_TEST %s %sABORTEDx1b[0m AT %sn",
unit_name,
cmc_test_color[2],
current_test);
#else
printf("UNIT_TEST %s ABORTED at %sn", unit_name, current_test);
#endif
else
#ifdef CMC_TEST_COLOR
printf("Test from %s %s%7sx1b[0m %sn",
unit_name,
passed ? cmc_test_color[0] : cmc_test_color[1],
passed ? "PASSED" : "FAILED",
current_test);
#else
printf("Test from %s %7s %sn", unit_name, passed ? "PASSED" : "FAILED", current_test);
#endif
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
void UNAME(void)
Unit Test Report : %-30s
#define CMC_CREATE_TEST(TNAME, BODY)
do
current_test = #TNAME;
tinfo.total += 1;
BODY;
while (0);
#define CMC_TEST_PASS()
do
tinfo.passed += 1;
if (tinfo.verbose)
cmc_test_log(unit_name, current_test, false, true);
while (0)
#define CMC_TEST_FAIL()
do
tinfo.failed += 1;
if (tinfo.verbose)
cmc_test_log(unit_name, current_test, false, false);
while (0)
#define CMC_TEST_PASS_ELSE_FAIL(EXPRESSION)
do
if (EXPRESSION)
CMC_TEST_PASS();
else
CMC_TEST_FAIL();
while (0)
#define CMC_TEST_ABORT()
do
tinfo.aborted = true;
tinfo.total -= 1;
goto unittest_abort;
while (0)
#endif /* CMC_TEST_H */
example.c
#include "test.h"
uintmax_t gcd(uintmax_t p, uintmax_t q)
if (p == 0)
return q;
if (q == 0)
return p;
uintmax_t r = p % q;
while (r != 0)
p = q;
q = r;
r = p % q;
return q;
CMC_CREATE_UNIT(gcd_test, true,
CMC_CREATE_TEST(edge_cases,
uintmax_t r1 = gcd(0, 0);
uintmax_t r2 = gcd(0, 9);
CMC_TEST_PASS_ELSE_FAIL(r1 == 0 && r2 == 9);
)
CMC_CREATE_TEST(test1,
CMC_TEST_PASS_ELSE_FAIL(gcd(42, 56) == 14);
)
CMC_CREATE_TEST(test3,
CMC_TEST_FAIL();
)
CMC_CREATE_TEST(test2,
uintmax_t r = gcd(12, 0);
if (r == 12)
CMC_TEST_ABORT();
else
CMC_TEST_PASS();
)
CMC_CREATE_TEST(test4,
uintmax_t r1 = gcd(461952, 116298);
uintmax_t r2 = gcd(7966496, 314080416);
CMC_TEST_PASS_ELSE_FAIL(r1 + r2 == 50);
)
)
int main(void)
gcd_test();
return 0;
This is part of the C Macro Collections library hosted here.
c unit-testing
$endgroup$
add a comment |
$begingroup$
Simple UnitTest macros utility in C. I decided to make this to be able to more easily organize my tests and I emphasized on readability. Please tell me what you think and if this utility would perform well in big unit tests.
timer.h
/* Simple timer macros utility */
#ifndef CMC_TIMER_H
#define CMC_TIMER_H
#include <time.h>
typedef struct timer_s
clock_t start;
clock_t stop;
double result;
timer_t;
#define TIMER_START(timer)
timer.start = clock();
#define TIMER_STOP(timer)
timer.stop = clock();
#define TIMER_CALC(timer)
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
#endif /* CMC_TIMER_H */
test.h
/**
* Simple Unit Test Utility
*
* CMC_CREATE_UNIT
* Create a UnitTest
* Parameters:
* - UNAME : UnitTest name (const char *)
* - VERBOSE : Print PASSED / FAILED messages if true (bool)
* - BODY : Body containing all tests related to this UnitTest
*
* CMC_CREATE_TEST
* Create a Test inside a UnitTest.
* Parameters:
* - TNAME : Test name (const char *)
* - BODY : Block of code containing a specific test
* Inside the BODY use the following macros:
* - CMC_TEST_PASS
* Pass current test.
* - CMC_TEST_FAIL
* Fail current test.
* - CMC_TEST_PASS_ELSE_FAIL
* If given expression is true, pass current test, else fail.
* - CMC_TEST_ABORT
* Abort current unit test and displays error message. All tests
* after the abort won't be called.
* All of these macros can only be called once per test.
*
* CMC_TEST_COLOR
* Define this macro to allow colored output.
*/
#ifndef CMC_TEST_H
#define CMC_TEST_H
#include "timer.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef CMC_TEST_COLOR
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
#endif
typedef struct cmc_test_info_s
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
bool aborted;
bool verbose;
cmc_test_info;
/* Logging function */
static void cmc_test_log(const char *unit_name, const char *current_test, bool aborted, bool passed)
if (aborted)
#ifdef CMC_TEST_COLOR
printf("UNIT_TEST %s %sABORTEDx1b[0m AT %sn",
unit_name,
cmc_test_color[2],
current_test);
#else
printf("UNIT_TEST %s ABORTED at %sn", unit_name, current_test);
#endif
else
#ifdef CMC_TEST_COLOR
printf("Test from %s %s%7sx1b[0m %sn",
unit_name,
passed ? cmc_test_color[0] : cmc_test_color[1],
passed ? "PASSED" : "FAILED",
current_test);
#else
printf("Test from %s %7s %sn", unit_name, passed ? "PASSED" : "FAILED", current_test);
#endif
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
void UNAME(void)
Unit Test Report : %-30s
#define CMC_CREATE_TEST(TNAME, BODY)
do
current_test = #TNAME;
tinfo.total += 1;
BODY;
while (0);
#define CMC_TEST_PASS()
do
tinfo.passed += 1;
if (tinfo.verbose)
cmc_test_log(unit_name, current_test, false, true);
while (0)
#define CMC_TEST_FAIL()
do
tinfo.failed += 1;
if (tinfo.verbose)
cmc_test_log(unit_name, current_test, false, false);
while (0)
#define CMC_TEST_PASS_ELSE_FAIL(EXPRESSION)
do
if (EXPRESSION)
CMC_TEST_PASS();
else
CMC_TEST_FAIL();
while (0)
#define CMC_TEST_ABORT()
do
tinfo.aborted = true;
tinfo.total -= 1;
goto unittest_abort;
while (0)
#endif /* CMC_TEST_H */
example.c
#include "test.h"
uintmax_t gcd(uintmax_t p, uintmax_t q)
if (p == 0)
return q;
if (q == 0)
return p;
uintmax_t r = p % q;
while (r != 0)
p = q;
q = r;
r = p % q;
return q;
CMC_CREATE_UNIT(gcd_test, true,
CMC_CREATE_TEST(edge_cases,
uintmax_t r1 = gcd(0, 0);
uintmax_t r2 = gcd(0, 9);
CMC_TEST_PASS_ELSE_FAIL(r1 == 0 && r2 == 9);
)
CMC_CREATE_TEST(test1,
CMC_TEST_PASS_ELSE_FAIL(gcd(42, 56) == 14);
)
CMC_CREATE_TEST(test3,
CMC_TEST_FAIL();
)
CMC_CREATE_TEST(test2,
uintmax_t r = gcd(12, 0);
if (r == 12)
CMC_TEST_ABORT();
else
CMC_TEST_PASS();
)
CMC_CREATE_TEST(test4,
uintmax_t r1 = gcd(461952, 116298);
uintmax_t r2 = gcd(7966496, 314080416);
CMC_TEST_PASS_ELSE_FAIL(r1 + r2 == 50);
)
)
int main(void)
gcd_test();
return 0;
This is part of the C Macro Collections library hosted here.
c unit-testing
$endgroup$
add a comment |
$begingroup$
Simple UnitTest macros utility in C. I decided to make this to be able to more easily organize my tests and I emphasized on readability. Please tell me what you think and if this utility would perform well in big unit tests.
timer.h
/* Simple timer macros utility */
#ifndef CMC_TIMER_H
#define CMC_TIMER_H
#include <time.h>
typedef struct timer_s
clock_t start;
clock_t stop;
double result;
timer_t;
#define TIMER_START(timer)
timer.start = clock();
#define TIMER_STOP(timer)
timer.stop = clock();
#define TIMER_CALC(timer)
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
#endif /* CMC_TIMER_H */
test.h
/**
* Simple Unit Test Utility
*
* CMC_CREATE_UNIT
* Create a UnitTest
* Parameters:
* - UNAME : UnitTest name (const char *)
* - VERBOSE : Print PASSED / FAILED messages if true (bool)
* - BODY : Body containing all tests related to this UnitTest
*
* CMC_CREATE_TEST
* Create a Test inside a UnitTest.
* Parameters:
* - TNAME : Test name (const char *)
* - BODY : Block of code containing a specific test
* Inside the BODY use the following macros:
* - CMC_TEST_PASS
* Pass current test.
* - CMC_TEST_FAIL
* Fail current test.
* - CMC_TEST_PASS_ELSE_FAIL
* If given expression is true, pass current test, else fail.
* - CMC_TEST_ABORT
* Abort current unit test and displays error message. All tests
* after the abort won't be called.
* All of these macros can only be called once per test.
*
* CMC_TEST_COLOR
* Define this macro to allow colored output.
*/
#ifndef CMC_TEST_H
#define CMC_TEST_H
#include "timer.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef CMC_TEST_COLOR
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
#endif
typedef struct cmc_test_info_s
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
bool aborted;
bool verbose;
cmc_test_info;
/* Logging function */
static void cmc_test_log(const char *unit_name, const char *current_test, bool aborted, bool passed)
if (aborted)
#ifdef CMC_TEST_COLOR
printf("UNIT_TEST %s %sABORTEDx1b[0m AT %sn",
unit_name,
cmc_test_color[2],
current_test);
#else
printf("UNIT_TEST %s ABORTED at %sn", unit_name, current_test);
#endif
else
#ifdef CMC_TEST_COLOR
printf("Test from %s %s%7sx1b[0m %sn",
unit_name,
passed ? cmc_test_color[0] : cmc_test_color[1],
passed ? "PASSED" : "FAILED",
current_test);
#else
printf("Test from %s %7s %sn", unit_name, passed ? "PASSED" : "FAILED", current_test);
#endif
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
void UNAME(void)
Unit Test Report : %-30s
#define CMC_CREATE_TEST(TNAME, BODY)
do
current_test = #TNAME;
tinfo.total += 1;
BODY;
while (0);
#define CMC_TEST_PASS()
do
tinfo.passed += 1;
if (tinfo.verbose)
cmc_test_log(unit_name, current_test, false, true);
while (0)
#define CMC_TEST_FAIL()
do
tinfo.failed += 1;
if (tinfo.verbose)
cmc_test_log(unit_name, current_test, false, false);
while (0)
#define CMC_TEST_PASS_ELSE_FAIL(EXPRESSION)
do
if (EXPRESSION)
CMC_TEST_PASS();
else
CMC_TEST_FAIL();
while (0)
#define CMC_TEST_ABORT()
do
tinfo.aborted = true;
tinfo.total -= 1;
goto unittest_abort;
while (0)
#endif /* CMC_TEST_H */
example.c
#include "test.h"
uintmax_t gcd(uintmax_t p, uintmax_t q)
if (p == 0)
return q;
if (q == 0)
return p;
uintmax_t r = p % q;
while (r != 0)
p = q;
q = r;
r = p % q;
return q;
CMC_CREATE_UNIT(gcd_test, true,
CMC_CREATE_TEST(edge_cases,
uintmax_t r1 = gcd(0, 0);
uintmax_t r2 = gcd(0, 9);
CMC_TEST_PASS_ELSE_FAIL(r1 == 0 && r2 == 9);
)
CMC_CREATE_TEST(test1,
CMC_TEST_PASS_ELSE_FAIL(gcd(42, 56) == 14);
)
CMC_CREATE_TEST(test3,
CMC_TEST_FAIL();
)
CMC_CREATE_TEST(test2,
uintmax_t r = gcd(12, 0);
if (r == 12)
CMC_TEST_ABORT();
else
CMC_TEST_PASS();
)
CMC_CREATE_TEST(test4,
uintmax_t r1 = gcd(461952, 116298);
uintmax_t r2 = gcd(7966496, 314080416);
CMC_TEST_PASS_ELSE_FAIL(r1 + r2 == 50);
)
)
int main(void)
gcd_test();
return 0;
This is part of the C Macro Collections library hosted here.
c unit-testing
$endgroup$
Simple UnitTest macros utility in C. I decided to make this to be able to more easily organize my tests and I emphasized on readability. Please tell me what you think and if this utility would perform well in big unit tests.
timer.h
/* Simple timer macros utility */
#ifndef CMC_TIMER_H
#define CMC_TIMER_H
#include <time.h>
typedef struct timer_s
clock_t start;
clock_t stop;
double result;
timer_t;
#define TIMER_START(timer)
timer.start = clock();
#define TIMER_STOP(timer)
timer.stop = clock();
#define TIMER_CALC(timer)
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
#endif /* CMC_TIMER_H */
test.h
/**
* Simple Unit Test Utility
*
* CMC_CREATE_UNIT
* Create a UnitTest
* Parameters:
* - UNAME : UnitTest name (const char *)
* - VERBOSE : Print PASSED / FAILED messages if true (bool)
* - BODY : Body containing all tests related to this UnitTest
*
* CMC_CREATE_TEST
* Create a Test inside a UnitTest.
* Parameters:
* - TNAME : Test name (const char *)
* - BODY : Block of code containing a specific test
* Inside the BODY use the following macros:
* - CMC_TEST_PASS
* Pass current test.
* - CMC_TEST_FAIL
* Fail current test.
* - CMC_TEST_PASS_ELSE_FAIL
* If given expression is true, pass current test, else fail.
* - CMC_TEST_ABORT
* Abort current unit test and displays error message. All tests
* after the abort won't be called.
* All of these macros can only be called once per test.
*
* CMC_TEST_COLOR
* Define this macro to allow colored output.
*/
#ifndef CMC_TEST_H
#define CMC_TEST_H
#include "timer.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef CMC_TEST_COLOR
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
#endif
typedef struct cmc_test_info_s
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
bool aborted;
bool verbose;
cmc_test_info;
/* Logging function */
static void cmc_test_log(const char *unit_name, const char *current_test, bool aborted, bool passed)
if (aborted)
#ifdef CMC_TEST_COLOR
printf("UNIT_TEST %s %sABORTEDx1b[0m AT %sn",
unit_name,
cmc_test_color[2],
current_test);
#else
printf("UNIT_TEST %s ABORTED at %sn", unit_name, current_test);
#endif
else
#ifdef CMC_TEST_COLOR
printf("Test from %s %s%7sx1b[0m %sn",
unit_name,
passed ? cmc_test_color[0] : cmc_test_color[1],
passed ? "PASSED" : "FAILED",
current_test);
#else
printf("Test from %s %7s %sn", unit_name, passed ? "PASSED" : "FAILED", current_test);
#endif
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
void UNAME(void)
Unit Test Report : %-30s
#define CMC_CREATE_TEST(TNAME, BODY)
do
current_test = #TNAME;
tinfo.total += 1;
BODY;
while (0);
#define CMC_TEST_PASS()
do
tinfo.passed += 1;
if (tinfo.verbose)
cmc_test_log(unit_name, current_test, false, true);
while (0)
#define CMC_TEST_FAIL()
do
tinfo.failed += 1;
if (tinfo.verbose)
cmc_test_log(unit_name, current_test, false, false);
while (0)
#define CMC_TEST_PASS_ELSE_FAIL(EXPRESSION)
do
if (EXPRESSION)
CMC_TEST_PASS();
else
CMC_TEST_FAIL();
while (0)
#define CMC_TEST_ABORT()
do
tinfo.aborted = true;
tinfo.total -= 1;
goto unittest_abort;
while (0)
#endif /* CMC_TEST_H */
example.c
#include "test.h"
uintmax_t gcd(uintmax_t p, uintmax_t q)
if (p == 0)
return q;
if (q == 0)
return p;
uintmax_t r = p % q;
while (r != 0)
p = q;
q = r;
r = p % q;
return q;
CMC_CREATE_UNIT(gcd_test, true,
CMC_CREATE_TEST(edge_cases,
uintmax_t r1 = gcd(0, 0);
uintmax_t r2 = gcd(0, 9);
CMC_TEST_PASS_ELSE_FAIL(r1 == 0 && r2 == 9);
)
CMC_CREATE_TEST(test1,
CMC_TEST_PASS_ELSE_FAIL(gcd(42, 56) == 14);
)
CMC_CREATE_TEST(test3,
CMC_TEST_FAIL();
)
CMC_CREATE_TEST(test2,
uintmax_t r = gcd(12, 0);
if (r == 12)
CMC_TEST_ABORT();
else
CMC_TEST_PASS();
)
CMC_CREATE_TEST(test4,
uintmax_t r1 = gcd(461952, 116298);
uintmax_t r2 = gcd(7966496, 314080416);
CMC_TEST_PASS_ELSE_FAIL(r1 + r2 == 50);
)
)
int main(void)
gcd_test();
return 0;
This is part of the C Macro Collections library hosted here.
c unit-testing
c unit-testing
edited Jun 26 at 4:35
LeoVen
asked Jun 26 at 4:14
LeoVenLeoVen
877 bronze badges
877 bronze badges
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
$begingroup$
Statement-like macros should normally be wrapped in do
...while(0)
. They should also avoid multiple expansion of arguments. So instead of
#define TIMER_START(timer)
timer.start = clock();
#define TIMER_STOP(timer)
timer.stop = clock();
#define TIMER_CALC(timer)
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
I'd recommend
#define TIMER_START(timer)
do (timer).start = clock(); while (0)
#define TIMER_STOP(timer)
do (timer).stop = clock(); while (0)
#define TIMER_CALC(timer)
do
struct timer_s* t = &(timer);
t->result = (double)(t->stop - t->start)
* 1000.0 / CLOCKS_PER_SEC;
while (0)
or (more likely) a set of real functions instead.
(I fixed the cast that was in the wrong place - it's the result of the subtraction that's an integer value that may lose precision when promoted to double
for the multiplication).
typedef struct timer_s timer_t
collides with a POSIX reserved identifier, so probably worth avoiding.
Please don't embed terminal-specific codes like this:
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
That becomes an impenetrable mess (or worse) on terminal types other than the ones you've considered. It's better to adapt to the actual known terminal type, perhaps using a library such as termcap.
It seems strange that VERBOSE
is a compile-time setting, rather than a run-time parameter here:
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
One of the statement-like macros has a stray semicolon after its definition:
#define CMC_CREATE_TEST(TNAME, BODY)
...
while (0);
+= 1
is more idiomatically written as ++
:
++tinfo.total;
Similarly, write -= 1
using --
.
We'd like the unit-test program to exit with success status only if all tests succeeded. If any fail, we want to know (e.g. to stop the build at that point). I suggest creating the test function with an int
return type to support this, and ending with return tinfo.aborted || tinfo.failed
.
The behaviour when CMC_TEST_ABORT()
is used is strange. It's the only code that's setting tinfo.aborted
, so we could move the if (tinfo.aborted)
block directly into that code. OTOH, we shouldn't be printing the elapsed time in that case, as we've skipped the TIMER_STOP(timer);
and TIMER_CALC(timer);
lines.
Finally, I know it's not really up for review, but I couldn't resist making observations on the gcd()
function used for testing:
There's no need for a specific p==0
test - the flow without the test already does the right thing (but the q==0
test is required, as it's used as divisor in the %
operation).
We can reduce duplication, by moving p % q
into the test:
uintmax_t gcd(uintmax_t p, uintmax_t q)
if (q == 0)
return p;
uintmax_t r;
while ((r = p % q) != 0)
p = q;
q = r;
return q;
$endgroup$
$begingroup$
Never heard ofdo ... while(0)
in the context of macros. Learnt something new/old again.
$endgroup$
– AlexV
Jun 26 at 11:02
$begingroup$
Could you add an example of how to improvestatic const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
with termcap?
$endgroup$
– Cacahuete Frito
Jun 26 at 14:34
$begingroup$
That's not something I ever do from C (I'm generally happy with plain output). If you're having trouble using termcap/terminfo, then Stack Overflow may be a better place to get help.
$endgroup$
– Toby Speight
Jun 26 at 14:43
$begingroup$
typedef struct timer_s timer_t
collides with a reserved identifier (all typedef names ending in _t are reserved for future Standard Library use). I was going to call that one, but I didn't because I didn't find the text in the Standard. Could you quote the specific section, please?
$endgroup$
– Cacahuete Frito
Jun 26 at 15:50
1
$begingroup$
I was using CPPReference, which cites its sources as C99 section 7.26 and C11 section K.3.1.2.
$endgroup$
– Toby Speight
Jun 26 at 15:55
|
show 2 more comments
$begingroup$
- __func__
__func__
exists for a reason; use it instead of const char *unit_name = #UNAME;
:
cmc_test_log(__func__, current_test, true, false);
...
printf("| Unit Test Report : %-30s|n", __func__);
- printf("%s", NULL);
Strictly speaking, that is Undefined Behaviour. glibc has a trick, and prints (null)
instead, but that trick is very unreliable, because if gcc decides to optimize printf
into puts
, then UB is invoked (read more: Adding newline character to printf() changes code behaviour on Stack Overflow).
So this code is probably invoking UB, given that all arguments are known at compile time, and the format string ends in n
(current_test
was defined here: const char *current_test = NULL;
(Why const
and NULL
???)).
#ifdef CMC_TEST_COLOR
printf("UNIT_TEST %s %sABORTEDx1b[0m AT %sn",
unit_name,
cmc_test_color[2],
current_test);
#else
- Differentiate typedef
ed identifiers from variable names
The easiest thing is to use _s
for struct
s (Or just don't typedef
at all) (not _t
; it is reserved by POSIX). You did it once, but forgot to do it in one of the typedef
s (cmc_test_info
).
Solution:
struct cmc_test_info_s
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
bool aborted;
bool verbose;
;
typedef struct cmc_test_info_s cmc_test_info_s;
- Function-like macros
Macros that behave like function calls should be named with lowercase to simulate functions, and help differentiate them of other macros that don't behave like functions:
#define timer_calc(timer) do
struct timer_s *t_ = timer;
double diff;
diff = t_->stop - t_->start;
t_->result = diff * 1000.0 / CLOCKS_PER_SEC;
while (0)
#define CMC_CREATE_UNIT(UNAME, verbose, BODY)
void UNAME(void)
...
Variables local to a macro should use names that are unlikely to be used in the calling function to avoid shadowing a variable (Imagine what would happen if the calling function called the macro this way: timer_calc(t_);
). The usual convention is to add a trailing underscore to names local to a macro.
- macros that depend on having a local variable with a magic name (source: Linux Kernel Coding Style)
#define FOO(val) bar(index, val)
might look like a good thing, but it’s confusing as hell when one reads the code and it’s prone to breakage from seemingly innocent changes.
Easy solution:
#define cmc_test_fail(tinfo, current_test) do
struct cmc_test_info_s tinfo_ = tinfo;
tinfo_.failed++;
if (tinfo_.verbose)
cmc_test_log(__func__, current_test, false, false);
while (0)
$endgroup$
$begingroup$
For "Whyconst
andNULL
" -- why not? It's notchar* const
, which would be a bit odd but would be a reasonable way to giveNULL
a better, context-specific name. It'sconst char*
.
$endgroup$
– Nic Hartley
Jun 26 at 18:04
$begingroup$
@NicHartley True, I interpreted it wrongly asconst char *const
. But the question was in the sense thatNULL
is not a validcurrent_test
value, and it would be better to give it some valid value, or leave it uninitialized, or ask the user to input a string as a variable. And because of that, I mixed some ideas :)
$endgroup$
– Cacahuete Frito
Jun 26 at 18:45
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f222954%2fsimple-unit-test-in-c%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Statement-like macros should normally be wrapped in do
...while(0)
. They should also avoid multiple expansion of arguments. So instead of
#define TIMER_START(timer)
timer.start = clock();
#define TIMER_STOP(timer)
timer.stop = clock();
#define TIMER_CALC(timer)
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
I'd recommend
#define TIMER_START(timer)
do (timer).start = clock(); while (0)
#define TIMER_STOP(timer)
do (timer).stop = clock(); while (0)
#define TIMER_CALC(timer)
do
struct timer_s* t = &(timer);
t->result = (double)(t->stop - t->start)
* 1000.0 / CLOCKS_PER_SEC;
while (0)
or (more likely) a set of real functions instead.
(I fixed the cast that was in the wrong place - it's the result of the subtraction that's an integer value that may lose precision when promoted to double
for the multiplication).
typedef struct timer_s timer_t
collides with a POSIX reserved identifier, so probably worth avoiding.
Please don't embed terminal-specific codes like this:
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
That becomes an impenetrable mess (or worse) on terminal types other than the ones you've considered. It's better to adapt to the actual known terminal type, perhaps using a library such as termcap.
It seems strange that VERBOSE
is a compile-time setting, rather than a run-time parameter here:
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
One of the statement-like macros has a stray semicolon after its definition:
#define CMC_CREATE_TEST(TNAME, BODY)
...
while (0);
+= 1
is more idiomatically written as ++
:
++tinfo.total;
Similarly, write -= 1
using --
.
We'd like the unit-test program to exit with success status only if all tests succeeded. If any fail, we want to know (e.g. to stop the build at that point). I suggest creating the test function with an int
return type to support this, and ending with return tinfo.aborted || tinfo.failed
.
The behaviour when CMC_TEST_ABORT()
is used is strange. It's the only code that's setting tinfo.aborted
, so we could move the if (tinfo.aborted)
block directly into that code. OTOH, we shouldn't be printing the elapsed time in that case, as we've skipped the TIMER_STOP(timer);
and TIMER_CALC(timer);
lines.
Finally, I know it's not really up for review, but I couldn't resist making observations on the gcd()
function used for testing:
There's no need for a specific p==0
test - the flow without the test already does the right thing (but the q==0
test is required, as it's used as divisor in the %
operation).
We can reduce duplication, by moving p % q
into the test:
uintmax_t gcd(uintmax_t p, uintmax_t q)
if (q == 0)
return p;
uintmax_t r;
while ((r = p % q) != 0)
p = q;
q = r;
return q;
$endgroup$
$begingroup$
Never heard ofdo ... while(0)
in the context of macros. Learnt something new/old again.
$endgroup$
– AlexV
Jun 26 at 11:02
$begingroup$
Could you add an example of how to improvestatic const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
with termcap?
$endgroup$
– Cacahuete Frito
Jun 26 at 14:34
$begingroup$
That's not something I ever do from C (I'm generally happy with plain output). If you're having trouble using termcap/terminfo, then Stack Overflow may be a better place to get help.
$endgroup$
– Toby Speight
Jun 26 at 14:43
$begingroup$
typedef struct timer_s timer_t
collides with a reserved identifier (all typedef names ending in _t are reserved for future Standard Library use). I was going to call that one, but I didn't because I didn't find the text in the Standard. Could you quote the specific section, please?
$endgroup$
– Cacahuete Frito
Jun 26 at 15:50
1
$begingroup$
I was using CPPReference, which cites its sources as C99 section 7.26 and C11 section K.3.1.2.
$endgroup$
– Toby Speight
Jun 26 at 15:55
|
show 2 more comments
$begingroup$
Statement-like macros should normally be wrapped in do
...while(0)
. They should also avoid multiple expansion of arguments. So instead of
#define TIMER_START(timer)
timer.start = clock();
#define TIMER_STOP(timer)
timer.stop = clock();
#define TIMER_CALC(timer)
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
I'd recommend
#define TIMER_START(timer)
do (timer).start = clock(); while (0)
#define TIMER_STOP(timer)
do (timer).stop = clock(); while (0)
#define TIMER_CALC(timer)
do
struct timer_s* t = &(timer);
t->result = (double)(t->stop - t->start)
* 1000.0 / CLOCKS_PER_SEC;
while (0)
or (more likely) a set of real functions instead.
(I fixed the cast that was in the wrong place - it's the result of the subtraction that's an integer value that may lose precision when promoted to double
for the multiplication).
typedef struct timer_s timer_t
collides with a POSIX reserved identifier, so probably worth avoiding.
Please don't embed terminal-specific codes like this:
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
That becomes an impenetrable mess (or worse) on terminal types other than the ones you've considered. It's better to adapt to the actual known terminal type, perhaps using a library such as termcap.
It seems strange that VERBOSE
is a compile-time setting, rather than a run-time parameter here:
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
One of the statement-like macros has a stray semicolon after its definition:
#define CMC_CREATE_TEST(TNAME, BODY)
...
while (0);
+= 1
is more idiomatically written as ++
:
++tinfo.total;
Similarly, write -= 1
using --
.
We'd like the unit-test program to exit with success status only if all tests succeeded. If any fail, we want to know (e.g. to stop the build at that point). I suggest creating the test function with an int
return type to support this, and ending with return tinfo.aborted || tinfo.failed
.
The behaviour when CMC_TEST_ABORT()
is used is strange. It's the only code that's setting tinfo.aborted
, so we could move the if (tinfo.aborted)
block directly into that code. OTOH, we shouldn't be printing the elapsed time in that case, as we've skipped the TIMER_STOP(timer);
and TIMER_CALC(timer);
lines.
Finally, I know it's not really up for review, but I couldn't resist making observations on the gcd()
function used for testing:
There's no need for a specific p==0
test - the flow without the test already does the right thing (but the q==0
test is required, as it's used as divisor in the %
operation).
We can reduce duplication, by moving p % q
into the test:
uintmax_t gcd(uintmax_t p, uintmax_t q)
if (q == 0)
return p;
uintmax_t r;
while ((r = p % q) != 0)
p = q;
q = r;
return q;
$endgroup$
$begingroup$
Never heard ofdo ... while(0)
in the context of macros. Learnt something new/old again.
$endgroup$
– AlexV
Jun 26 at 11:02
$begingroup$
Could you add an example of how to improvestatic const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
with termcap?
$endgroup$
– Cacahuete Frito
Jun 26 at 14:34
$begingroup$
That's not something I ever do from C (I'm generally happy with plain output). If you're having trouble using termcap/terminfo, then Stack Overflow may be a better place to get help.
$endgroup$
– Toby Speight
Jun 26 at 14:43
$begingroup$
typedef struct timer_s timer_t
collides with a reserved identifier (all typedef names ending in _t are reserved for future Standard Library use). I was going to call that one, but I didn't because I didn't find the text in the Standard. Could you quote the specific section, please?
$endgroup$
– Cacahuete Frito
Jun 26 at 15:50
1
$begingroup$
I was using CPPReference, which cites its sources as C99 section 7.26 and C11 section K.3.1.2.
$endgroup$
– Toby Speight
Jun 26 at 15:55
|
show 2 more comments
$begingroup$
Statement-like macros should normally be wrapped in do
...while(0)
. They should also avoid multiple expansion of arguments. So instead of
#define TIMER_START(timer)
timer.start = clock();
#define TIMER_STOP(timer)
timer.stop = clock();
#define TIMER_CALC(timer)
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
I'd recommend
#define TIMER_START(timer)
do (timer).start = clock(); while (0)
#define TIMER_STOP(timer)
do (timer).stop = clock(); while (0)
#define TIMER_CALC(timer)
do
struct timer_s* t = &(timer);
t->result = (double)(t->stop - t->start)
* 1000.0 / CLOCKS_PER_SEC;
while (0)
or (more likely) a set of real functions instead.
(I fixed the cast that was in the wrong place - it's the result of the subtraction that's an integer value that may lose precision when promoted to double
for the multiplication).
typedef struct timer_s timer_t
collides with a POSIX reserved identifier, so probably worth avoiding.
Please don't embed terminal-specific codes like this:
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
That becomes an impenetrable mess (or worse) on terminal types other than the ones you've considered. It's better to adapt to the actual known terminal type, perhaps using a library such as termcap.
It seems strange that VERBOSE
is a compile-time setting, rather than a run-time parameter here:
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
One of the statement-like macros has a stray semicolon after its definition:
#define CMC_CREATE_TEST(TNAME, BODY)
...
while (0);
+= 1
is more idiomatically written as ++
:
++tinfo.total;
Similarly, write -= 1
using --
.
We'd like the unit-test program to exit with success status only if all tests succeeded. If any fail, we want to know (e.g. to stop the build at that point). I suggest creating the test function with an int
return type to support this, and ending with return tinfo.aborted || tinfo.failed
.
The behaviour when CMC_TEST_ABORT()
is used is strange. It's the only code that's setting tinfo.aborted
, so we could move the if (tinfo.aborted)
block directly into that code. OTOH, we shouldn't be printing the elapsed time in that case, as we've skipped the TIMER_STOP(timer);
and TIMER_CALC(timer);
lines.
Finally, I know it's not really up for review, but I couldn't resist making observations on the gcd()
function used for testing:
There's no need for a specific p==0
test - the flow without the test already does the right thing (but the q==0
test is required, as it's used as divisor in the %
operation).
We can reduce duplication, by moving p % q
into the test:
uintmax_t gcd(uintmax_t p, uintmax_t q)
if (q == 0)
return p;
uintmax_t r;
while ((r = p % q) != 0)
p = q;
q = r;
return q;
$endgroup$
Statement-like macros should normally be wrapped in do
...while(0)
. They should also avoid multiple expansion of arguments. So instead of
#define TIMER_START(timer)
timer.start = clock();
#define TIMER_STOP(timer)
timer.stop = clock();
#define TIMER_CALC(timer)
timer.result = (double)(((timer.stop - timer.start) * 1000.0) / CLOCKS_PER_SEC);
I'd recommend
#define TIMER_START(timer)
do (timer).start = clock(); while (0)
#define TIMER_STOP(timer)
do (timer).stop = clock(); while (0)
#define TIMER_CALC(timer)
do
struct timer_s* t = &(timer);
t->result = (double)(t->stop - t->start)
* 1000.0 / CLOCKS_PER_SEC;
while (0)
or (more likely) a set of real functions instead.
(I fixed the cast that was in the wrong place - it's the result of the subtraction that's an integer value that may lose precision when promoted to double
for the multiplication).
typedef struct timer_s timer_t
collides with a POSIX reserved identifier, so probably worth avoiding.
Please don't embed terminal-specific codes like this:
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
That becomes an impenetrable mess (or worse) on terminal types other than the ones you've considered. It's better to adapt to the actual known terminal type, perhaps using a library such as termcap.
It seems strange that VERBOSE
is a compile-time setting, rather than a run-time parameter here:
#define CMC_CREATE_UNIT(UNAME, VERBOSE, BODY)
One of the statement-like macros has a stray semicolon after its definition:
#define CMC_CREATE_TEST(TNAME, BODY)
...
while (0);
+= 1
is more idiomatically written as ++
:
++tinfo.total;
Similarly, write -= 1
using --
.
We'd like the unit-test program to exit with success status only if all tests succeeded. If any fail, we want to know (e.g. to stop the build at that point). I suggest creating the test function with an int
return type to support this, and ending with return tinfo.aborted || tinfo.failed
.
The behaviour when CMC_TEST_ABORT()
is used is strange. It's the only code that's setting tinfo.aborted
, so we could move the if (tinfo.aborted)
block directly into that code. OTOH, we shouldn't be printing the elapsed time in that case, as we've skipped the TIMER_STOP(timer);
and TIMER_CALC(timer);
lines.
Finally, I know it's not really up for review, but I couldn't resist making observations on the gcd()
function used for testing:
There's no need for a specific p==0
test - the flow without the test already does the right thing (but the q==0
test is required, as it's used as divisor in the %
operation).
We can reduce duplication, by moving p % q
into the test:
uintmax_t gcd(uintmax_t p, uintmax_t q)
if (q == 0)
return p;
uintmax_t r;
while ((r = p % q) != 0)
p = q;
q = r;
return q;
edited Jun 26 at 16:45
answered Jun 26 at 8:31
Toby SpeightToby Speight
30.5k7 gold badges45 silver badges132 bronze badges
30.5k7 gold badges45 silver badges132 bronze badges
$begingroup$
Never heard ofdo ... while(0)
in the context of macros. Learnt something new/old again.
$endgroup$
– AlexV
Jun 26 at 11:02
$begingroup$
Could you add an example of how to improvestatic const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
with termcap?
$endgroup$
– Cacahuete Frito
Jun 26 at 14:34
$begingroup$
That's not something I ever do from C (I'm generally happy with plain output). If you're having trouble using termcap/terminfo, then Stack Overflow may be a better place to get help.
$endgroup$
– Toby Speight
Jun 26 at 14:43
$begingroup$
typedef struct timer_s timer_t
collides with a reserved identifier (all typedef names ending in _t are reserved for future Standard Library use). I was going to call that one, but I didn't because I didn't find the text in the Standard. Could you quote the specific section, please?
$endgroup$
– Cacahuete Frito
Jun 26 at 15:50
1
$begingroup$
I was using CPPReference, which cites its sources as C99 section 7.26 and C11 section K.3.1.2.
$endgroup$
– Toby Speight
Jun 26 at 15:55
|
show 2 more comments
$begingroup$
Never heard ofdo ... while(0)
in the context of macros. Learnt something new/old again.
$endgroup$
– AlexV
Jun 26 at 11:02
$begingroup$
Could you add an example of how to improvestatic const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
with termcap?
$endgroup$
– Cacahuete Frito
Jun 26 at 14:34
$begingroup$
That's not something I ever do from C (I'm generally happy with plain output). If you're having trouble using termcap/terminfo, then Stack Overflow may be a better place to get help.
$endgroup$
– Toby Speight
Jun 26 at 14:43
$begingroup$
typedef struct timer_s timer_t
collides with a reserved identifier (all typedef names ending in _t are reserved for future Standard Library use). I was going to call that one, but I didn't because I didn't find the text in the Standard. Could you quote the specific section, please?
$endgroup$
– Cacahuete Frito
Jun 26 at 15:50
1
$begingroup$
I was using CPPReference, which cites its sources as C99 section 7.26 and C11 section K.3.1.2.
$endgroup$
– Toby Speight
Jun 26 at 15:55
$begingroup$
Never heard of
do ... while(0)
in the context of macros. Learnt something new/old again.$endgroup$
– AlexV
Jun 26 at 11:02
$begingroup$
Never heard of
do ... while(0)
in the context of macros. Learnt something new/old again.$endgroup$
– AlexV
Jun 26 at 11:02
$begingroup$
Could you add an example of how to improve
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
with termcap?$endgroup$
– Cacahuete Frito
Jun 26 at 14:34
$begingroup$
Could you add an example of how to improve
static const char *cmc_test_color[] = "x1b[32m", "x1b[31m", "x1b[35m";
with termcap?$endgroup$
– Cacahuete Frito
Jun 26 at 14:34
$begingroup$
That's not something I ever do from C (I'm generally happy with plain output). If you're having trouble using termcap/terminfo, then Stack Overflow may be a better place to get help.
$endgroup$
– Toby Speight
Jun 26 at 14:43
$begingroup$
That's not something I ever do from C (I'm generally happy with plain output). If you're having trouble using termcap/terminfo, then Stack Overflow may be a better place to get help.
$endgroup$
– Toby Speight
Jun 26 at 14:43
$begingroup$
typedef struct timer_s timer_t
collides with a reserved identifier (all typedef names ending in _t are reserved for future Standard Library use). I was going to call that one, but I didn't because I didn't find the text in the Standard. Could you quote the specific section, please?$endgroup$
– Cacahuete Frito
Jun 26 at 15:50
$begingroup$
typedef struct timer_s timer_t
collides with a reserved identifier (all typedef names ending in _t are reserved for future Standard Library use). I was going to call that one, but I didn't because I didn't find the text in the Standard. Could you quote the specific section, please?$endgroup$
– Cacahuete Frito
Jun 26 at 15:50
1
1
$begingroup$
I was using CPPReference, which cites its sources as C99 section 7.26 and C11 section K.3.1.2.
$endgroup$
– Toby Speight
Jun 26 at 15:55
$begingroup$
I was using CPPReference, which cites its sources as C99 section 7.26 and C11 section K.3.1.2.
$endgroup$
– Toby Speight
Jun 26 at 15:55
|
show 2 more comments
$begingroup$
- __func__
__func__
exists for a reason; use it instead of const char *unit_name = #UNAME;
:
cmc_test_log(__func__, current_test, true, false);
...
printf("| Unit Test Report : %-30s|n", __func__);
- printf("%s", NULL);
Strictly speaking, that is Undefined Behaviour. glibc has a trick, and prints (null)
instead, but that trick is very unreliable, because if gcc decides to optimize printf
into puts
, then UB is invoked (read more: Adding newline character to printf() changes code behaviour on Stack Overflow).
So this code is probably invoking UB, given that all arguments are known at compile time, and the format string ends in n
(current_test
was defined here: const char *current_test = NULL;
(Why const
and NULL
???)).
#ifdef CMC_TEST_COLOR
printf("UNIT_TEST %s %sABORTEDx1b[0m AT %sn",
unit_name,
cmc_test_color[2],
current_test);
#else
- Differentiate typedef
ed identifiers from variable names
The easiest thing is to use _s
for struct
s (Or just don't typedef
at all) (not _t
; it is reserved by POSIX). You did it once, but forgot to do it in one of the typedef
s (cmc_test_info
).
Solution:
struct cmc_test_info_s
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
bool aborted;
bool verbose;
;
typedef struct cmc_test_info_s cmc_test_info_s;
- Function-like macros
Macros that behave like function calls should be named with lowercase to simulate functions, and help differentiate them of other macros that don't behave like functions:
#define timer_calc(timer) do
struct timer_s *t_ = timer;
double diff;
diff = t_->stop - t_->start;
t_->result = diff * 1000.0 / CLOCKS_PER_SEC;
while (0)
#define CMC_CREATE_UNIT(UNAME, verbose, BODY)
void UNAME(void)
...
Variables local to a macro should use names that are unlikely to be used in the calling function to avoid shadowing a variable (Imagine what would happen if the calling function called the macro this way: timer_calc(t_);
). The usual convention is to add a trailing underscore to names local to a macro.
- macros that depend on having a local variable with a magic name (source: Linux Kernel Coding Style)
#define FOO(val) bar(index, val)
might look like a good thing, but it’s confusing as hell when one reads the code and it’s prone to breakage from seemingly innocent changes.
Easy solution:
#define cmc_test_fail(tinfo, current_test) do
struct cmc_test_info_s tinfo_ = tinfo;
tinfo_.failed++;
if (tinfo_.verbose)
cmc_test_log(__func__, current_test, false, false);
while (0)
$endgroup$
$begingroup$
For "Whyconst
andNULL
" -- why not? It's notchar* const
, which would be a bit odd but would be a reasonable way to giveNULL
a better, context-specific name. It'sconst char*
.
$endgroup$
– Nic Hartley
Jun 26 at 18:04
$begingroup$
@NicHartley True, I interpreted it wrongly asconst char *const
. But the question was in the sense thatNULL
is not a validcurrent_test
value, and it would be better to give it some valid value, or leave it uninitialized, or ask the user to input a string as a variable. And because of that, I mixed some ideas :)
$endgroup$
– Cacahuete Frito
Jun 26 at 18:45
add a comment |
$begingroup$
- __func__
__func__
exists for a reason; use it instead of const char *unit_name = #UNAME;
:
cmc_test_log(__func__, current_test, true, false);
...
printf("| Unit Test Report : %-30s|n", __func__);
- printf("%s", NULL);
Strictly speaking, that is Undefined Behaviour. glibc has a trick, and prints (null)
instead, but that trick is very unreliable, because if gcc decides to optimize printf
into puts
, then UB is invoked (read more: Adding newline character to printf() changes code behaviour on Stack Overflow).
So this code is probably invoking UB, given that all arguments are known at compile time, and the format string ends in n
(current_test
was defined here: const char *current_test = NULL;
(Why const
and NULL
???)).
#ifdef CMC_TEST_COLOR
printf("UNIT_TEST %s %sABORTEDx1b[0m AT %sn",
unit_name,
cmc_test_color[2],
current_test);
#else
- Differentiate typedef
ed identifiers from variable names
The easiest thing is to use _s
for struct
s (Or just don't typedef
at all) (not _t
; it is reserved by POSIX). You did it once, but forgot to do it in one of the typedef
s (cmc_test_info
).
Solution:
struct cmc_test_info_s
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
bool aborted;
bool verbose;
;
typedef struct cmc_test_info_s cmc_test_info_s;
- Function-like macros
Macros that behave like function calls should be named with lowercase to simulate functions, and help differentiate them of other macros that don't behave like functions:
#define timer_calc(timer) do
struct timer_s *t_ = timer;
double diff;
diff = t_->stop - t_->start;
t_->result = diff * 1000.0 / CLOCKS_PER_SEC;
while (0)
#define CMC_CREATE_UNIT(UNAME, verbose, BODY)
void UNAME(void)
...
Variables local to a macro should use names that are unlikely to be used in the calling function to avoid shadowing a variable (Imagine what would happen if the calling function called the macro this way: timer_calc(t_);
). The usual convention is to add a trailing underscore to names local to a macro.
- macros that depend on having a local variable with a magic name (source: Linux Kernel Coding Style)
#define FOO(val) bar(index, val)
might look like a good thing, but it’s confusing as hell when one reads the code and it’s prone to breakage from seemingly innocent changes.
Easy solution:
#define cmc_test_fail(tinfo, current_test) do
struct cmc_test_info_s tinfo_ = tinfo;
tinfo_.failed++;
if (tinfo_.verbose)
cmc_test_log(__func__, current_test, false, false);
while (0)
$endgroup$
$begingroup$
For "Whyconst
andNULL
" -- why not? It's notchar* const
, which would be a bit odd but would be a reasonable way to giveNULL
a better, context-specific name. It'sconst char*
.
$endgroup$
– Nic Hartley
Jun 26 at 18:04
$begingroup$
@NicHartley True, I interpreted it wrongly asconst char *const
. But the question was in the sense thatNULL
is not a validcurrent_test
value, and it would be better to give it some valid value, or leave it uninitialized, or ask the user to input a string as a variable. And because of that, I mixed some ideas :)
$endgroup$
– Cacahuete Frito
Jun 26 at 18:45
add a comment |
$begingroup$
- __func__
__func__
exists for a reason; use it instead of const char *unit_name = #UNAME;
:
cmc_test_log(__func__, current_test, true, false);
...
printf("| Unit Test Report : %-30s|n", __func__);
- printf("%s", NULL);
Strictly speaking, that is Undefined Behaviour. glibc has a trick, and prints (null)
instead, but that trick is very unreliable, because if gcc decides to optimize printf
into puts
, then UB is invoked (read more: Adding newline character to printf() changes code behaviour on Stack Overflow).
So this code is probably invoking UB, given that all arguments are known at compile time, and the format string ends in n
(current_test
was defined here: const char *current_test = NULL;
(Why const
and NULL
???)).
#ifdef CMC_TEST_COLOR
printf("UNIT_TEST %s %sABORTEDx1b[0m AT %sn",
unit_name,
cmc_test_color[2],
current_test);
#else
- Differentiate typedef
ed identifiers from variable names
The easiest thing is to use _s
for struct
s (Or just don't typedef
at all) (not _t
; it is reserved by POSIX). You did it once, but forgot to do it in one of the typedef
s (cmc_test_info
).
Solution:
struct cmc_test_info_s
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
bool aborted;
bool verbose;
;
typedef struct cmc_test_info_s cmc_test_info_s;
- Function-like macros
Macros that behave like function calls should be named with lowercase to simulate functions, and help differentiate them of other macros that don't behave like functions:
#define timer_calc(timer) do
struct timer_s *t_ = timer;
double diff;
diff = t_->stop - t_->start;
t_->result = diff * 1000.0 / CLOCKS_PER_SEC;
while (0)
#define CMC_CREATE_UNIT(UNAME, verbose, BODY)
void UNAME(void)
...
Variables local to a macro should use names that are unlikely to be used in the calling function to avoid shadowing a variable (Imagine what would happen if the calling function called the macro this way: timer_calc(t_);
). The usual convention is to add a trailing underscore to names local to a macro.
- macros that depend on having a local variable with a magic name (source: Linux Kernel Coding Style)
#define FOO(val) bar(index, val)
might look like a good thing, but it’s confusing as hell when one reads the code and it’s prone to breakage from seemingly innocent changes.
Easy solution:
#define cmc_test_fail(tinfo, current_test) do
struct cmc_test_info_s tinfo_ = tinfo;
tinfo_.failed++;
if (tinfo_.verbose)
cmc_test_log(__func__, current_test, false, false);
while (0)
$endgroup$
- __func__
__func__
exists for a reason; use it instead of const char *unit_name = #UNAME;
:
cmc_test_log(__func__, current_test, true, false);
...
printf("| Unit Test Report : %-30s|n", __func__);
- printf("%s", NULL);
Strictly speaking, that is Undefined Behaviour. glibc has a trick, and prints (null)
instead, but that trick is very unreliable, because if gcc decides to optimize printf
into puts
, then UB is invoked (read more: Adding newline character to printf() changes code behaviour on Stack Overflow).
So this code is probably invoking UB, given that all arguments are known at compile time, and the format string ends in n
(current_test
was defined here: const char *current_test = NULL;
(Why const
and NULL
???)).
#ifdef CMC_TEST_COLOR
printf("UNIT_TEST %s %sABORTEDx1b[0m AT %sn",
unit_name,
cmc_test_color[2],
current_test);
#else
- Differentiate typedef
ed identifiers from variable names
The easiest thing is to use _s
for struct
s (Or just don't typedef
at all) (not _t
; it is reserved by POSIX). You did it once, but forgot to do it in one of the typedef
s (cmc_test_info
).
Solution:
struct cmc_test_info_s
uintmax_t total;
uintmax_t passed;
uintmax_t failed;
bool aborted;
bool verbose;
;
typedef struct cmc_test_info_s cmc_test_info_s;
- Function-like macros
Macros that behave like function calls should be named with lowercase to simulate functions, and help differentiate them of other macros that don't behave like functions:
#define timer_calc(timer) do
struct timer_s *t_ = timer;
double diff;
diff = t_->stop - t_->start;
t_->result = diff * 1000.0 / CLOCKS_PER_SEC;
while (0)
#define CMC_CREATE_UNIT(UNAME, verbose, BODY)
void UNAME(void)
...
Variables local to a macro should use names that are unlikely to be used in the calling function to avoid shadowing a variable (Imagine what would happen if the calling function called the macro this way: timer_calc(t_);
). The usual convention is to add a trailing underscore to names local to a macro.
- macros that depend on having a local variable with a magic name (source: Linux Kernel Coding Style)
#define FOO(val) bar(index, val)
might look like a good thing, but it’s confusing as hell when one reads the code and it’s prone to breakage from seemingly innocent changes.
Easy solution:
#define cmc_test_fail(tinfo, current_test) do
struct cmc_test_info_s tinfo_ = tinfo;
tinfo_.failed++;
if (tinfo_.verbose)
cmc_test_log(__func__, current_test, false, false);
while (0)
edited Jun 26 at 17:35
answered Jun 26 at 15:31
Cacahuete FritoCacahuete Frito
53312 bronze badges
53312 bronze badges
$begingroup$
For "Whyconst
andNULL
" -- why not? It's notchar* const
, which would be a bit odd but would be a reasonable way to giveNULL
a better, context-specific name. It'sconst char*
.
$endgroup$
– Nic Hartley
Jun 26 at 18:04
$begingroup$
@NicHartley True, I interpreted it wrongly asconst char *const
. But the question was in the sense thatNULL
is not a validcurrent_test
value, and it would be better to give it some valid value, or leave it uninitialized, or ask the user to input a string as a variable. And because of that, I mixed some ideas :)
$endgroup$
– Cacahuete Frito
Jun 26 at 18:45
add a comment |
$begingroup$
For "Whyconst
andNULL
" -- why not? It's notchar* const
, which would be a bit odd but would be a reasonable way to giveNULL
a better, context-specific name. It'sconst char*
.
$endgroup$
– Nic Hartley
Jun 26 at 18:04
$begingroup$
@NicHartley True, I interpreted it wrongly asconst char *const
. But the question was in the sense thatNULL
is not a validcurrent_test
value, and it would be better to give it some valid value, or leave it uninitialized, or ask the user to input a string as a variable. And because of that, I mixed some ideas :)
$endgroup$
– Cacahuete Frito
Jun 26 at 18:45
$begingroup$
For "Why
const
and NULL
" -- why not? It's not char* const
, which would be a bit odd but would be a reasonable way to give NULL
a better, context-specific name. It's const char*
.$endgroup$
– Nic Hartley
Jun 26 at 18:04
$begingroup$
For "Why
const
and NULL
" -- why not? It's not char* const
, which would be a bit odd but would be a reasonable way to give NULL
a better, context-specific name. It's const char*
.$endgroup$
– Nic Hartley
Jun 26 at 18:04
$begingroup$
@NicHartley True, I interpreted it wrongly as
const char *const
. But the question was in the sense that NULL
is not a valid current_test
value, and it would be better to give it some valid value, or leave it uninitialized, or ask the user to input a string as a variable. And because of that, I mixed some ideas :)$endgroup$
– Cacahuete Frito
Jun 26 at 18:45
$begingroup$
@NicHartley True, I interpreted it wrongly as
const char *const
. But the question was in the sense that NULL
is not a valid current_test
value, and it would be better to give it some valid value, or leave it uninitialized, or ask the user to input a string as a variable. And because of that, I mixed some ideas :)$endgroup$
– Cacahuete Frito
Jun 26 at 18:45
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f222954%2fsimple-unit-test-in-c%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown