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;








8












$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.










share|improve this question











$endgroup$


















    8












    $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.










    share|improve this question











    $endgroup$














      8












      8








      8





      $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.










      share|improve this question











      $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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jun 26 at 4:35







      LeoVen

















      asked Jun 26 at 4:14









      LeoVenLeoVen

      877 bronze badges




      877 bronze badges




















          2 Answers
          2






          active

          oldest

          votes


















          8












          $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;






          share|improve this answer











          $endgroup$












          • $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$
            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


















          3












          $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 typedefed identifiers from variable names



          The easiest thing is to use _s for structs (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 typedefs (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)





          share|improve this answer











          $endgroup$












          • $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














          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
          );



          );













          draft saved

          draft discarded


















          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









          8












          $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;






          share|improve this answer











          $endgroup$












          • $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$
            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















          8












          $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;






          share|improve this answer











          $endgroup$












          • $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$
            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













          8












          8








          8





          $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;






          share|improve this answer











          $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;







          share|improve this answer














          share|improve this answer



          share|improve this answer








          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 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$
            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$
            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$
            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













          3












          $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 typedefed identifiers from variable names



          The easiest thing is to use _s for structs (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 typedefs (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)





          share|improve this answer











          $endgroup$












          • $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
















          3












          $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 typedefed identifiers from variable names



          The easiest thing is to use _s for structs (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 typedefs (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)





          share|improve this answer











          $endgroup$












          • $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














          3












          3








          3





          $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 typedefed identifiers from variable names



          The easiest thing is to use _s for structs (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 typedefs (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)





          share|improve this answer











          $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 typedefed identifiers from variable names



          The easiest thing is to use _s for structs (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 typedefs (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)






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jun 26 at 17:35

























          answered Jun 26 at 15:31









          Cacahuete FritoCacahuete Frito

          53312 bronze badges




          53312 bronze badges











          • $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$
            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$
          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


















          draft saved

          draft discarded
















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Get product attribute by attribute group code in magento 2get product attribute by product attribute group in magento 2Magento 2 Log Bundle Product Data in List Page?How to get all product attribute of a attribute group of Default attribute set?Magento 2.1 Create a filter in the product grid by new attributeMagento 2 : Get Product Attribute values By GroupMagento 2 How to get all existing values for one attributeMagento 2 get custom attribute of a single product inside a pluginMagento 2.3 How to get all the Multi Source Inventory (MSI) locations collection in custom module?Magento2: how to develop rest API to get new productsGet product attribute by attribute group code ( [attribute_group_code] ) in magento 2

          Category:9 (number) SubcategoriesMedia in category "9 (number)"Navigation menuUpload mediaGND ID: 4485639-8Library of Congress authority ID: sh85091979ReasonatorScholiaStatistics

          Magento 2.3: How do i solve this, Not registered handle, on custom form?How can i rewrite TierPrice Block in Magento2magento 2 captcha not rendering if I override layout xmlmain.CRITICAL: Plugin class doesn't existMagento 2 : Problem while adding custom button order view page?Magento 2.2.5: Overriding Admin Controller sales/orderMagento 2.2.5: Add, Update and Delete existing products Custom OptionsMagento 2.3 : File Upload issue in UI Component FormMagento2 Not registered handleHow to configured Form Builder Js in my custom magento 2.3.0 module?Magento 2.3. How to create image upload field in an admin form