Search code examples
cprintfbuffercheck-framework

stop printf printing twice running check framework in C?


  1. Probably a deep explanation of how printing in general in c and c threads happen would solve the issue
  2. There aren't loops and scanf used at any point
  3. Because my code uses check framework I would assume there is some forking going on.
  4. Most likely it is something to do with the buffer so I am using fflush(stdout), In case it is because of something inside the framework, I'd appreciate somebody who knows the framework will explain what happens.
  5. check framework page check.sourceforge.net
  6. Git link if this may help somehow https://github.com/batousik/Practical-C2
  7. Link to build/test log, at line 438 printing starts https://travis-ci.org/batousik/Practical-C2/builds/53513707

Code sample

START_TEST(test_START_EMPTY_TREE_TREEBASE_PRINT_FREETREE_TEST) {
    printf("_________START_EMPTY_TREE/TREEBASE_PRINT/FREETREE_TEST__________\n");
    fflush(stdout);

    int *ptr;
    for (int i = 0; i < arr_size; i++) {
        ptr = malloc(sizeof(int));
        memcpy(ptr, (int_arr_ptr + i), sizeof(int));
        insert(ptr_tree_base_int_1, ptr);
    }
    // should print tree
    printf("!!!Next lines has to be tree printed out\n");
    fflush(stdout);
    ck_assert_int_eq(printTree(ptr_tree_base_int_1), true);
    printf("_____________________\n");
    fflush(stdout);

    // No output
    ck_assert_int_eq(freeTree(ptr_tree_base_int_1), true);
    // should print cannot free empty tree
    printf("!!!Next line has to be:\"cannot free empty tree\"\n");
    fflush(stdout);
    ck_assert_int_eq(freeTree(ptr_tree_base_int_1), true);

    // should print cannot print empty tree
    printf("!!!Next line has to be:\"cannot print empty tree\"\n");
    fflush(stdout);
    ck_assert_int_eq(printTree(ptr_tree_base_int_1), false);
    printf("_____________________\n");
    fflush(stdout);

    ptr_tree_base_int_1 = NULL;
    free(ptr_tree_base_int_1);

    // should print cannot free empty tree base
    printf("!!!Next line has to be:\"cannot free empty tree base\"\n");
    fflush(stdout);
    ck_assert_int_eq(freeTree(ptr_tree_base_int_1), true);

    // should print cannot print empty tree base
    printf("!!!Next line has to be:\"cannot print empty tree base\"\n");
    fflush(stdout);
    ck_assert_int_eq(printTree(ptr_tree_base_int_1), false);
    printf("_____________________\n");
    fflush(stdout);

    free(int_arr_ptr);
    int_arr_ptr = NULL;
    printf("freeing the array...\n");
    fflush(stdout);
    printf("________END_____________\n");
    fflush(stdout);
    ck_assert_int_eq(ptr_tree_base_int_1->size, 0);
} END_TEST

PrintTree function

bool printTree(TreeBase *tree){
void *previous = NULL;
int cnt_tasks = 0;
if (!tree) {
    printf("PRINT: Cannot print empty tree base\n");
    fflush(stdout);
    return false;
}
/* set current to root of binary tree */
TreeNode *current_node = tree->base;
if (!(tree->base)) {
    printf("PRINT: Cannot print empty tree\n");
    fflush(stdout);
    return false;
}
StackNode *stack = NULL;
while (true) {
    if(current_node) {
        push(&stack, current_node);
        current_node = current_node->left;
    } else {
        if (stack) {
            current_node = pop(&stack);
            if (cnt_tasks > 1) {
                if (tree->comp(previous, current_node->value) != -1) {
                    printf("PRINTTREE: Invalid BST\n");
                    fflush(stdout);
                    assert(NULL);
                }
            }
            previous = current_node->value;
            tree->print(current_node->value);
            cnt_tasks++;
            current_node = current_node->right;
        } else {
            return (cnt_tasks == tree->size);
        }
    }
}

}

tree->print function

void print_ints(void *p){
   printf("%d\n", *(int*)p);
   fflush(stdout);
}

Sample output

    FREETREE: Cant free empty tree
    !!!Next line has to be:"cannot free empty tree"
    FREETREE: Cant free empty tree
    FREETREE: Cant free empty tree
    !!!Next line has to be:"cannot print empty tree"
    PRINT: Cannot print empty tree
    PRINT: Cannot print empty tree
    _____________________

That is a lot of code, Main point is after !!! there should be one line but two printed

    printf("PRINT: Cannot print empty tree\n");
    fflush(stdout);
    return false;

^^ This line prints twice

For comments: what is "Macros"? and this is from the API

#define     ck_assert_int_eq(X, Y)   _ck_assert_int(X, ==, Y)

Solution

  • The ck_assert_int_eq macro introduced in version 0.9.6 of the Check unit testing framework was defined as:

    #define _ck_assert_int(X, O, Y) ck_assert_msg((X) O (Y), \
      "Assertion '"#X#O#Y"' failed: "#X"==%d, "#Y"==%d", X, Y) 
    #define ck_assert_int_eq(X, Y) _ck_assert_int(X, ==, Y) 
    

    As can be seen, the arguments appear twice in the macro definition: one time for the actual equality check (the (X) O (Y) part), and another as the last arguments to ck_assert_msg(..., X, Y) for the purpose of logging a message in case of failure. Looking at a specific part of your code:

    // should print cannot print empty tree
    printf("!!!Next line has to be:\"cannot print empty tree\"\n");
    fflush(stdout);
    ck_assert_int_eq(printTree(ptr_tree_base_int_1), false);
    

    the C-preprocessor would correspondingly expand the macro to code equivalent to:

    // should print cannot free empty tree
    printf("!!!Next line has to be:\"cannot free empty tree\"\n");
    fflush(stdout);
    ck_assert_msg((printTree(ptr_tree_base_int_1)) == (false), 
      "Assertion '"#X#O#Y"' failed: "#X"==%d, "#Y"==%d", 
      printTree(ptr_tree_base_int_1), false);
    

    The printTree function is thus called twice with all the associated side effects, including printing the line "PRINT: Cannot print empty tree" twice. One way to protect yourself against this kind of error is to make sure arguments to macros have no possible side effects. This can be accomplished in your case, by moving the call to printTree outside the macro:

    bool printTreeResult;
    printTreeResult = printTree(ptr_tree_base_int_1);
    ck_assert_int_eq(printTreeResult, false);
    

    Note that since this kind of macro is generally error prone (as you've just experienced), the definition of the ck_assert_int_eq was improved in version 0.9.9 to:

    #define _ck_assert_int(X, OP, Y) do { \
      int _ck_x = (X); \
      int _ck_y = (Y); \
      ck_assert_msg(_ck_x OP _ck_y, \
        "Assertion '"#X#OP#Y"' failed: "#X"==%d, "#Y"==%d", _ck_x, _ck_y); \
    } while (0)
    #define ck_assert_int_eq(X, Y) _ck_assert_int(X, ==, Y)
    

    Correspondingly, another way to fix the problem with duplicated output lines would be to upgrade your installation of the Check unit testing framework to a more recent version (i.e. at least 0.9.9), if that's an option.