Common Unit Testing Code - Nachverfolgung

Sep 02 2020

Diese Frage ist eine Folgefrage zum Common Unit Testing Code-Teil meiner lexikalischen Analysatorfragen.

Mein Hauptanliegen ist der Code in der Header-Datei und der C-Quelldatei, die strdup () implementiert. Da das Programm, zu dem dieser Code gehört, plattformübergreifend ist, muss es entweder unter Windows oder Linux kompiliert und ausgeführt werden und sollte mit beiden kompatibel sein. Die strdup()Funktion ist Teil des C2X C-Standards. Wenn sie verfügbar ist, sollte der Code weiterhin kompiliert werden und funktionieren. Die #defines in der Header-Datei basieren auf der gccVersion von string.h.

Ein sekundäres Problem ist die Leistung. Viele der Parameter wurden in const geändert. Die Mitglieder der Test_Log_Data-Struktur wurden neu angeordnet, um die Speichernutzung zu verbessern.

Ein drittes Problem war die archaische Verwendung. Das externe vor den Funktionsprototypen wurde in allen Header-Dateien entfernt, nicht nur in common_unit_test_logic.h.

Der Originalcode dient zum Vergleich.

Neuer Code

common_unit_test_logic.h

#ifndef COMMON_UNIT_TEST_LOGIC_H
#define COMMON_UNIT_TEST_LOGIC_H
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#ifndef REDUCED_VM_AND_HRF_DEPENDENCIES
#include "human_readable_program_format.h"
#endif

typedef struct test_log_data
{
    const char* function_name;
    char* path;
    bool status;
    bool stand_alone;
} Test_Log_Data;

extern FILE* error_out_file;
extern FILE* unit_test_log_file;

bool init_vm_error_reporting(const char* error_log_file_name);
#ifndef REDUCED_VM_AND_HRF_DEPENDENCIES
Human_Readable_Program_Format* default_program(size_t* program_size);
#endif

#ifndef strdup
#ifdef _MSC_VER
#if _MSC_VER > 1920
#define strdup _strdup
#endif
#else
#define strdup mystrdup      
#endif
#endif

char* mystrdup(const char* string_to_copy);
unsigned char* ucstrdup(const unsigned char* string_to_copy);
void disengage_error_reporting(void);
bool init_unit_tests(const char* log_file_name);
void report_error_generic(const char* error_message);
void report_create_and_init_test_log_data_memory_failure(const char* function_name);
void log_test_status_each_step(const char* function_name, const bool status, const char* path, const bool stand_alone);
void init_test_log_data(Test_Log_Data* log_data, const char* function_name, const bool status, char* path, const bool stand_alone);
Test_Log_Data* create_and_init_test_log_data(const char* function_name, const bool status, char* path, const bool stand_alone);
void log_test_status_each_step2(const Test_Log_Data* test_data_to_log);
void log_start_positive_path(const char* function_name);
void log_start_positive_path2(const Test_Log_Data* log_data);
void log_start_test_path(const Test_Log_Data* log_data);
void log_end_test_path(const Test_Log_Data* log_data);
void log_end_positive_path(const char* function_name);
void log_end_positive_path2(const Test_Log_Data* log_data);
void log_start_negative_path(const char* function_name);
void log_end_negative_path(const char* function_name);
void log_generic_message(const char *log_message);
void close_unit_tests(void);

#endif // !COMMON_UNIT_TEST_LOGIC_H

common_unit_test_logic.c

#include "common_unit_test_logic.h"
#ifndef REDUCED_VM_AND_HRF_DEPENDENCIES
#include "virtual_machine.h"
#endif
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE* error_out_file = NULL;
FILE* unit_test_log_file = NULL;


char* mystrdup(const char* string_to_copy)
{
    char* return_string = NULL;
    size_t length = strlen(string_to_copy);
    ++length;

    return_string = calloc(length, sizeof(*return_string));
    if (return_string)
    {
        memcpy(return_string, string_to_copy, length - 1);
    }

    return return_string;
}

unsigned char* ucstrdup(const unsigned char* string_to_copy)
{
    unsigned char* return_string = NULL;
    size_t length = strlen((const char *)string_to_copy);
    ++length;

    return_string = calloc(length, sizeof(*return_string));
    if (return_string)
    {
        memcpy(return_string, string_to_copy, length - 1);
    }

    return return_string;
}

bool init_vm_error_reporting(const char* error_log_file_name)
{
    bool status_is_good = true;

    if (error_log_file_name)
    {
        error_out_file = fopen(error_log_file_name, "w");
        if (!error_out_file)
        {
            error_out_file = stderr;
            fprintf(error_out_file, "Can't open error output file, %s", "error_log_file_name");
            status_is_good = false;
        }
    }
    else
    {
        error_out_file = stderr;
    }

    return status_is_good;
}

void disengage_error_reporting(void)
{
    if (error_out_file != stderr)
    {
        fclose(error_out_file);
    }
}

#ifndef REDUCED_VM_AND_HRF_DEPENDENCIES
/*
 * Allow unit tests that don't require virtual_machine.c and human_readable_program_format.c.
 */
Human_Readable_Program_Format* default_program(size_t* program_size)
{
    Human_Readable_Program_Format program[] =
    {
        {PUSH, 0x0A},
        {PUSH, 0x43},
        {PUSH, 0x42},
        {PUSH, 0x41},
        {OUTPUTCHAR, 0x00},
        {POP, 0x00},
        {OUTPUTCHAR, 0x00},
        {POP, 0x00},
        {OUTPUTCHAR, 0x00},
        {POP, 0x00},
        {HALT, 0x00}
    };

    size_t progsize = sizeof(program) / sizeof(*program);

    Human_Readable_Program_Format* copy_of_program = duplicate_program(program, progsize);
    if (copy_of_program)
    {
        *program_size = progsize;
    }

    return copy_of_program;
}
#endif

bool init_unit_tests(const char* log_file_name)
{
    if (log_file_name)
    {
        unit_test_log_file = fopen(log_file_name, "w");
        if (!unit_test_log_file)
        {
            fprintf(error_out_file, "Can't open %s for output\n", log_file_name);
            return false;
        }
        error_out_file = unit_test_log_file;
    }
    else
    {
        unit_test_log_file = stdout;
        error_out_file = stderr;
    }

    return true;
}

void report_error_generic(const char *error_message)
{
    fprintf(error_out_file, "%s\n", error_message);
}

void close_unit_tests(void)
{
    if (unit_test_log_file != stdout)
    {
        fclose(unit_test_log_file);
    }
}

static bool log_test_is_positive_path(const Test_Log_Data* log_data)
{
    bool is_positive = true;

    if (!log_data->path)
    {
        fprintf(error_out_file, "Programmer error: log_data->path is NULL in log_test_is_positive_path()\n");
        return false;
    }

    char* string_to_test = strdup(log_data->path);
    if (!string_to_test)
    {
        fprintf(error_out_file, "Memory Allocation error: strdup() failed in log_test_is_positive_path()\n");
        fprintf(error_out_file, "Exiting program.\n");
        exit(EXIT_FAILURE);
    }

    char* stt_ptr = string_to_test;
    while (*stt_ptr)
    {
        *stt_ptr = (char) toupper(*stt_ptr);
        stt_ptr++;
    }

    is_positive = (strcmp(string_to_test, "POSITIVE") == 0);
    free(string_to_test);

    return is_positive;
}

void log_test_status_each_step(const char* function_name, const bool status, const char* path, const bool stand_alone)
{
    if (stand_alone)
    {
        fprintf(unit_test_log_file, "%s(): %s Path %s\n", function_name, path,
            (status) ? "Passed" : "Failed");
    }
}

void log_test_status_each_step2(const Test_Log_Data *test_data_to_log)
{
    if (test_data_to_log->stand_alone)
    {
        fprintf(unit_test_log_file, "%s(): %s Path %s\n", test_data_to_log->function_name,
            test_data_to_log->path, (test_data_to_log->status) ? "Passed" : "Failed");
    }
}

void log_start_positive_path(const char* function_name)
{
    fprintf(unit_test_log_file, "\nStarting POSITIVE PATH testing for %s\n\n",
        function_name);
}

void log_start_positive_path2(const Test_Log_Data *log_data)
{
    fprintf(unit_test_log_file, "\nStarting POSITIVE PATH testing for %s\n\n",
        log_data->function_name);
}

void log_end_positive_path(const char* function_name)
{
    fprintf(unit_test_log_file, "\nEnding POSITIVE PATH testing for %s\n", function_name);
}

void log_end_positive_path2(const Test_Log_Data* log_data)
{
    fprintf(unit_test_log_file, "\nEnding POSITIVE PATH testing for %s, POSITIVE PATH  %s \n",
        log_data->function_name, log_data->status? "PASSED" : "FAILED");
}

void log_start_negative_path(const char* function_name)
{
    fprintf(unit_test_log_file, "\nStarting NEGATIVE PATH testing for %s\n\n", function_name);
}

void log_end_negative_path(const char* function_name)
{
    fprintf(unit_test_log_file, "\nEnding NEGATIVE PATH testing for %s\n", function_name);
    fflush(unit_test_log_file);        // Current unit test is done flush the output.
}

void log_start_test_path(const Test_Log_Data* log_data)
{
    bool is_positive = log_test_is_positive_path(log_data);

    fprintf(unit_test_log_file, "\nStarting %s PATH testing for %s\n\n",
        is_positive ? "POSITIVE" : "NEGATIVE", log_data->function_name);
}

void log_end_test_path(const Test_Log_Data *log_data)
{
    bool is_positive = log_test_is_positive_path(log_data);

    fprintf(unit_test_log_file, "\nEnding %s PATH testing for %s, Path %s\n",
        is_positive ? "POSITIVE" : "NEGATIVE", log_data->function_name,
        log_data->status ? "PASSED" : "FAILED");

    if (!is_positive)
    {
        fflush(unit_test_log_file);        // Current unit test is done flush the output.
    }
}

void log_generic_message(const char* log_message)
{
    fprintf(unit_test_log_file, log_message);
}

void init_test_log_data(Test_Log_Data* log_data, const char *function_name, const bool status, char *path, bool stand_alone)
{
    log_data->function_name = function_name;
    log_data->status = status;
    log_data->path = path;
    log_data->stand_alone = stand_alone;
}

Test_Log_Data *create_and_init_test_log_data(const char* function_name, const bool status, char* path, const bool stand_alone)
{
    Test_Log_Data* log_data = calloc(1, sizeof(*log_data));
    if (log_data)
    {
        init_test_log_data(log_data, function_name, status, path, stand_alone);
    }
    else
    {
        fprintf(error_out_file, "In %s calloc() failed\n", "create_and_init_test_log_data");
    }

    return log_data;
}

// provides common error report for memory allocation error.
void report_create_and_init_test_log_data_memory_failure(const char *function_name)
{
    fprintf(error_out_file, "In function %s, Memory allocation failed in create_and_init_test_log_data\n", function_name);
}

Originalcode :

common_unit_test_logic.h

#ifndef COMMON_UNIT_TEST_LOGIC_H
#define COMMON_UNIT_TEST_LOGIC_H
#include <stdio.h>
#include <stdbool.h>
#ifndef REDUCED_VM_AND_HRF_DEPENDENCIES
#include "human_readable_program_format.h"
#endif

typedef struct test_log_data
{
    char* function_name;
    bool status;
    char* path;
    bool stand_alone;
} Test_Log_Data;

extern FILE* error_out_file;
extern FILE* unit_test_log_file;

extern bool init_vm_error_reporting(char* error_log_file_name);
#ifndef REDUCED_VM_AND_HRF_DEPENDENCIES
extern Human_Readable_Program_Format* default_program(size_t* program_size);
#endif
extern void disengage_error_reporting(void);
extern bool init_unit_tests(char* log_file_name);
extern void report_error_generic(char* error_message);
extern void report_create_and_init_test_log_data_memory_failure(char* function_name);
extern void log_test_status_each_step(char* function_name, bool status, char* path, bool stand_alone);
extern void init_test_log_data(Test_Log_Data* log_data, char* function_name, bool status, char* path, bool stand_alone);
extern Test_Log_Data* create_and_init_test_log_data(char* function_name, bool status, char* path, bool stand_alone);
extern void log_test_status_each_step2(Test_Log_Data* test_data_to_log);
extern void log_start_positive_path(char* function_name);
extern void log_start_positive_path2(Test_Log_Data* log_data);
extern void log_start_test_path(Test_Log_Data* log_data);
extern void log_end_test_path(Test_Log_Data* log_data);
extern void log_end_positive_path(char* function_name);
extern void log_end_positive_path2(Test_Log_Data* log_data);
extern void log_start_negative_path(char* function_name);
extern void log_end_negative_path(char* function_name);
extern void log_generic_message(char *log_message);
extern void close_unit_tests(void);

#endif // !COMMON_UNIT_TEST_LOGIC_H

common_unit_test_logic.c

#include "common_unit_test_logic.h"
#ifndef REDUCED_VM_AND_HRF_DEPENDENCIES
#include "virtual_machine.h"
#endif
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE* error_out_file = NULL;
FILE* unit_test_log_file = NULL;

bool init_vm_error_reporting(char* error_log_file_name)
{
    bool status_is_good = true;

    if (error_log_file_name)
    {
        error_out_file = fopen(error_log_file_name, "w");
        if (!error_out_file)
        {
            error_out_file = stderr;
            fprintf(error_out_file, "Can't open error output file, %s", "error_log_file_name");
            status_is_good = false;
        }
    }
    else
    {
        error_out_file = stderr;
    }

    return status_is_good;
}

void disengage_error_reporting(void)
{
    if (error_out_file != stderr)
    {
        fclose(error_out_file);
    }
}

#ifndef REDUCED_VM_AND_HRF_DEPENDENCIES
/*
 * Allow unit tests that don't require virtual_machine.c and human_readable_program_format.c.
 */
Human_Readable_Program_Format* default_program(size_t* program_size)
{
    Human_Readable_Program_Format program[] =
    {
        {PUSH, 0x0A},
        {PUSH, 0x43},
        {PUSH, 0x42},
        {PUSH, 0x41},
        {OUTPUTCHAR, 0x00},
        {POP, 0x00},
        {OUTPUTCHAR, 0x00},
        {POP, 0x00},
        {OUTPUTCHAR, 0x00},
        {POP, 0x00},
        {HALT, 0x00}
    };

    size_t progsize = sizeof(program) / sizeof(*program);

    Human_Readable_Program_Format* copy_of_program = duplicate_program(program, progsize);
    if (copy_of_program)
    {
        *program_size = progsize;
    }

    return copy_of_program;
}
#endif

bool init_unit_tests(char* log_file_name)
{
    if (log_file_name)
    {
        unit_test_log_file = fopen(log_file_name, "w");
        if (!unit_test_log_file)
        {
            fprintf(error_out_file, "Can't open %s for output\n", log_file_name);
            return false;
        }
        error_out_file = unit_test_log_file;
    }
    else
    {
        unit_test_log_file = stdout;
        error_out_file = stderr;
    }

    return true;
}

void report_error_generic(char *error_message)
{
    fprintf(error_out_file, "%s\n", error_message);
}

void close_unit_tests(void)
{
    if (unit_test_log_file != stdout)
    {
        fclose(unit_test_log_file);
    }
}

static bool log_test_is_positive_path(Test_Log_Data* log_data)
{
    bool is_positive = true;

    if (!log_data->path)
    {
        fprintf(error_out_file, "Programmer error: log_data->path is NULL in log_test_is_positive_path()\n");
        return false;
    }

    char* string_to_test = _strdup(log_data->path);
    if (!string_to_test)
    {
        fprintf(error_out_file, "Memory Allocation error: _strdup() failed in log_test_is_positive_path()\n");
        fprintf(error_out_file, "Exiting program.\n");
        exit(EXIT_FAILURE);
    }

    char* stt_ptr = string_to_test;
    while (*stt_ptr)
    {
        *stt_ptr = (char) toupper(*stt_ptr);
        stt_ptr++;
    }

    is_positive = (strcmp(string_to_test, "POSITIVE") == 0);

    return is_positive;
}

void log_test_status_each_step(char* function_name, bool status, char* path, bool stand_alone)
{
    if (stand_alone)
    {
        fprintf(unit_test_log_file, "%s(): %s Path %s\n", function_name, path,
            (status) ? "Passed" : "Failed");
    }
}

void log_test_status_each_step2(Test_Log_Data *test_data_to_log)
{
    if (test_data_to_log->stand_alone)
    {
        fprintf(unit_test_log_file, "%s(): %s Path %s\n", test_data_to_log->function_name,
            test_data_to_log->path, (test_data_to_log->status) ? "Passed" : "Failed");
    }
}

void log_start_positive_path(char* function_name)
{
    fprintf(unit_test_log_file, "\nStarting POSITIVE PATH testing for %s\n\n",
        function_name);
}

void log_start_positive_path2(Test_Log_Data *log_data)
{
    fprintf(unit_test_log_file, "\nStarting POSITIVE PATH testing for %s\n\n",
        log_data->function_name);
}

void log_end_positive_path(char* function_name)
{
    fprintf(unit_test_log_file, "\nEnding POSITIVE PATH testing for %s\n", function_name);
}

void log_end_positive_path2(Test_Log_Data* log_data)
{
    fprintf(unit_test_log_file, "\nEnding POSITIVE PATH testing for %s, POSITIVE PATH  %s \n",
        log_data->function_name, log_data->status? "PASSED" : "FAILED");
}

void log_start_negative_path(char* function_name)
{
    fprintf(unit_test_log_file, "\nStarting NEGATIVE PATH testing for %s\n\n", function_name);
}

void log_end_negative_path(char* function_name)
{
    fprintf(unit_test_log_file, "\nEnding NEGATIVE PATH testing for %s\n", function_name);
    fflush(unit_test_log_file);        // Current unit test is done flush the output.
}

void log_start_test_path(Test_Log_Data* log_data)
{
    bool is_positive = log_test_is_positive_path(log_data);

    fprintf(unit_test_log_file, "\nStarting %s PATH testing for %s\n\n",
        is_positive ? "POSITIVE" : "NEGATIVE", log_data->function_name);
}

void log_end_test_path(Test_Log_Data *log_data)
{
    bool is_positive = log_test_is_positive_path(log_data);

    fprintf(unit_test_log_file, "\nEnding %s PATH testing for %s, Path %s\n",
        is_positive ? "POSITIVE" : "NEGATIVE", log_data->function_name,
        log_data->status ? "PASSED" : "FAILED");

    if (!is_positive)
    {
        fflush(unit_test_log_file);        // Current unit test is done flush the output.
    }
}

void log_generic_message(char* log_message)
{
    fprintf(unit_test_log_file, log_message);
}

void init_test_log_data(Test_Log_Data* log_data, char *function_name, bool status, char *path, bool stand_alone)
{
    log_data->function_name = function_name;
    log_data->status = status;
    log_data->path = path;
    log_data->stand_alone = stand_alone;
}

Test_Log_Data *create_and_init_test_log_data(char* function_name, bool status, char* path, bool stand_alone)
{
    Test_Log_Data* log_data = calloc(1, sizeof(*log_data));
    if (log_data)
    {
        init_test_log_data(log_data, function_name, status, path, stand_alone);
    }
    else
    {
        fprintf(error_out_file, "In %s calloc() failed\n", "create_and_init_test_log_data");
    }

    return log_data;
}

// provides common error report for memory allocation error.
void report_create_and_init_test_log_data_memory_failure(char *function_name)
{
    fprintf(error_out_file, "In function %s, Memory allocation failed in create_and_init_test_log_data\n", function_name);
}

Antworten

2 chux-ReinstateMonica Sep 08 2020 at 22:55

mystrdup()hat ein Manko: Um * nix-artig zu sein, würde ich erwarten, Fälle zu erkennen, die sich einstellen könnten errno.

IMO, benutze malloc()und kopiere auch das Nullzeichen .

Ab wann ist es eine gute Idee, strdup zu verwenden (vs malloc / strcpy)

#include <errno.h>
#include <stdlib.h>

char *mystrdup(const char *s) {
  // Optional test, s should point to a string
  if (s == NULL) { 
    #ifdef EINVAL
      // For systems that support this "invalid argument" errno
      errno = EINVAL;
    #ednif
    return NULL;  
  }
  size_t siz = strlen(s) + 1;
  char *y = malloc(siz);
  if (y != NULL) {
    memcpy(y, s, siz);
  } else {
    #ifdef ENOMEM
      // For systems that support this "out-of-memory" errno
      errno = ENOMEM;
    #else
      ;
    #endif
  }
  return y;
}
2 pacmaninbw Sep 03 2020 at 20:10

Die Dateien common_unit_test_logic.*sind zu komplex.

Die common_unit_test_logic C - Datei und Header - Datei folgen nicht der einzige Verantwortung Prinzip , dass die Staaten

… Dass jedes Modul, jede Klasse oder Funktion die Verantwortung für einen einzelnen Teil der von der Software bereitgestellten Funktionalität haben sollte und dass die Verantwortung vollständig von diesem Modul, dieser Klasse oder Funktion übernommen werden sollte.

Dies zwang unnötige # ifdef- und # ifndef-Anweisungen in den Code. Dies wurde durch den Bruch behoben common_unit_test_logic.cund common_unit_test_logic.hin drei separate Modulen, error_reporting, my_strdup, und unit_test_logging.

Nur das unit_test_loggingModul befindet sich noch im Common_UnitTest_CodeVerzeichnis unter dem UnitTestsVerzeichnis. Das error_reportingModul und das my_strdupModul wurden beide in das VMWithEditorQuellcodeverzeichnis verschoben, sodass sie sowohl für das Primärprojekt als auch für die Projekte mit mehreren Komponententests freigegeben werden können. Ein viertes Modul default_programwurde auch für das Hauptprogramm und einige der anderen Komponententests erstellt. Der Code war ifdef'de aus dem Einheitentest des lexikalischen Analysators.

Das Aufteilen des Codes ermöglicht eine stärkere Wiederverwendung der einzelnen Module, erfordert jedoch zusätzliche #includeAnweisungen in vielen Dateien.

Die getrennten Module:

my_strdup.h

#ifndef MY_STRDUP_H
#define MY_STRDUP_H

#include <string.h>

#ifndef strdup
#ifdef _MSC_VER
#if _MSC_VER > 1920
#define strdup _strdup
#endif
#else
#define strdup mystrdup      
#endif
#endif

char* mystrdup(const char* string_to_copy);
unsigned char* ucstrdup(const unsigned char* string_to_copy);

#endif    // MY_STRDUP_H

my_strdup.c

#include "my_strdup.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* mystrdup(const char* string_to_copy)
{
    char* return_string = NULL;
    size_t length = strlen(string_to_copy);
    ++length;

    return_string = calloc(length, sizeof(*return_string));
    if (return_string)
    {
        memcpy(return_string, string_to_copy, length - 1);
    }

    return return_string;
}

unsigned char* ucstrdup(const unsigned char* string_to_copy)
{
    unsigned char* return_string = NULL;
    size_t length = strlen((const char*)string_to_copy);
    ++length;

    return_string = calloc(length, sizeof(*return_string));
    if (return_string)
    {
        memcpy(return_string, string_to_copy, length - 1);
    }

    return return_string;
}

error_reporting.h

#ifndef ERROR_REPORTING_H
#define ERROR_REPORTING_H

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

extern FILE* error_out_file;

bool init_vm_error_reporting(const char* error_log_file_name);
void disengage_error_reporting(void);
void report_error_generic(const char* error_message);

#endif    // !ERROR_REPORTING_H

error_reporting.c

#ifndef ERROR_REPORTING_C
#define ERROR_REPORTING_C

#include "error_reporting.h"
#ifdef UNIT_TESTING
#include "unit_test_logging.h"
#endif    // UNIT_TESTING
#include <stdio.h>

FILE* error_out_file = NULL;

bool init_vm_error_reporting(const char* error_log_file_name)
{
    bool status_is_good = true;

    if (error_log_file_name)
    {
        error_out_file = fopen(error_log_file_name, "w");
        if (!error_out_file)
        {
#ifdef UNIT_TESTING
            error_out_file = stderr;
#endif    // UNIT_TESTING
            fprintf(error_out_file, "Can't open error output file, %s", "error_log_file_name");
            status_is_good = false;
        }
    }
    else
    {
        error_out_file = stderr;
    }

    return status_is_good;
}

void disengage_error_reporting(void)
{
    if (error_out_file != stderr)
    {
        fclose(error_out_file);
    }
}

void report_error_generic(const char *error_message)
{
    fprintf(error_out_file, "%s\n", error_message);
}

#endif    // !ERROR_REPORTING_C

default_program.h

#ifndef DEFAULT_PROGRAM_H
#define DEFAULT_PROGRAM_H

#include "human_readable_program_format.h"
#include <stdint.h>

Human_Readable_Program_Format* default_program(size_t* program_size);


#endif    // DEFAULT_PROGRAM_H

default_program.c

#ifndef DEFAULT_PROGRAM_C
#define DEFAULT_PROGRAM_C

#include "human_readable_program_format.h"
#include "default_program.h"
#include <stdint.h>

Human_Readable_Program_Format* default_program(size_t* program_size)
{
    Human_Readable_Program_Format program[] =
    {
        {PUSH, 0x0A},
        {PUSH, 0x43},
        {PUSH, 0x42},
        {PUSH, 0x41},
        {OUTPUTCHAR, 0x00},
        {POP, 0x00},
        {OUTPUTCHAR, 0x00},
        {POP, 0x00},
        {OUTPUTCHAR, 0x00},
        {POP, 0x00},
        {HALT, 0x00}
    };

    size_t progsize = sizeof(program) / sizeof(*program);

    Human_Readable_Program_Format* copy_of_program = duplicate_program(program, progsize);
    if (copy_of_program)
    {
        *program_size = progsize;
    }

    return copy_of_program;
}

#endif    // DEFAULT_PROGRAM_C

unit_test_logging.h

#ifndef UNIT_TEST_LOGGING_H
#define UNIT_TEST_LOGGING_H
#include <stdio.h>
#include <stdbool.h>

typedef struct test_log_data
{
    const char* function_name;
    char* path;
    bool status;
    bool stand_alone;
} Test_Log_Data;

extern FILE* unit_test_log_file;

bool init_unit_tests(const char* log_file_name);
void report_create_and_init_test_log_data_memory_failure(const char* function_name);
void log_test_status_each_step(const char* function_name, const bool status, const char* path, const bool stand_alone);
void init_test_log_data(Test_Log_Data* log_data, const char* function_name, const bool status, char* path, const bool stand_alone);
Test_Log_Data* create_and_init_test_log_data(const char* function_name, const bool status, char* path, const bool stand_alone);
void log_test_status_each_step2(const Test_Log_Data* test_data_to_log);
void log_start_positive_path(const char* function_name);
void log_start_positive_path2(const Test_Log_Data* log_data);
void log_start_test_path(const Test_Log_Data* log_data);
void log_end_test_path(const Test_Log_Data* log_data);
void log_end_positive_path(const char* function_name);
void log_end_positive_path2(const Test_Log_Data* log_data);
void log_start_negative_path(const char* function_name);
void log_end_negative_path(const char* function_name);
void log_generic_message(const char *log_message);
void close_unit_tests(void);

#endif // !UNIT_TEST_LOGGING_H

unit_test_logging.c

#include "error_reporting.h"
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE* unit_test_log_file = NULL;


bool init_unit_tests(const char* log_file_name)
{
    if (log_file_name)
    {
        unit_test_log_file = fopen(log_file_name, "w");
        if (!unit_test_log_file)
        {
            fprintf(error_out_file, "Can't open %s for output\n", log_file_name);
            return false;
        }
        error_out_file = unit_test_log_file;
    }
    else
    {
        unit_test_log_file = stdout;
        error_out_file = stderr;
    }

    return true;
}

void close_unit_tests(void)
{
    if (unit_test_log_file != stdout)
    {
        fclose(unit_test_log_file);
    }
}

static bool log_test_is_positive_path(const Test_Log_Data* log_data)
{
    bool is_positive = true;

    if (!log_data->path)
    {
        fprintf(error_out_file, "Programmer error: log_data->path is NULL in log_test_is_positive_path()\n");
        return false;
    }

    char* string_to_test = strdup(log_data->path);
    if (!string_to_test)
    {
        fprintf(error_out_file, "Memory Allocation error: strdup() failed in log_test_is_positive_path()\n");
        fprintf(error_out_file, "Exiting program.\n");
        exit(EXIT_FAILURE);
    }

    char* stt_ptr = string_to_test;
    while (*stt_ptr)
    {
        *stt_ptr = (char) toupper(*stt_ptr);
        stt_ptr++;
    }

    is_positive = (strcmp(string_to_test, "POSITIVE") == 0);
    free(string_to_test);

    return is_positive;
}

void log_test_status_each_step(const char* function_name, const bool status, const char* path, const bool stand_alone)
{
    if (stand_alone)
    {
        fprintf(unit_test_log_file, "%s(): %s Path %s\n", function_name, path,
            (status) ? "Passed" : "Failed");
    }
}

void log_test_status_each_step2(const Test_Log_Data *test_data_to_log)
{
    if (test_data_to_log->stand_alone)
    {
        fprintf(unit_test_log_file, "%s(): %s Path %s\n", test_data_to_log->function_name,
            test_data_to_log->path, (test_data_to_log->status) ? "Passed" : "Failed");
    }
}

void log_start_positive_path(const char* function_name)
{
    fprintf(unit_test_log_file, "\nStarting POSITIVE PATH testing for %s\n\n",
        function_name);
}

void log_start_positive_path2(const Test_Log_Data *log_data)
{
    fprintf(unit_test_log_file, "\nStarting POSITIVE PATH testing for %s\n\n",
        log_data->function_name);
}

void log_end_positive_path(const char* function_name)
{
    fprintf(unit_test_log_file, "\nEnding POSITIVE PATH testing for %s\n", function_name);
}

void log_end_positive_path2(const Test_Log_Data* log_data)
{
    fprintf(unit_test_log_file, "\nEnding POSITIVE PATH testing for %s, POSITIVE PATH  %s \n",
        log_data->function_name, log_data->status? "PASSED" : "FAILED");
}

void log_start_negative_path(const char* function_name)
{
    fprintf(unit_test_log_file, "\nStarting NEGATIVE PATH testing for %s\n\n", function_name);
}

void log_end_negative_path(const char* function_name)
{
    fprintf(unit_test_log_file, "\nEnding NEGATIVE PATH testing for %s\n", function_name);
    fflush(unit_test_log_file);        // Current unit test is done flush the output.
}

void log_start_test_path(const Test_Log_Data* log_data)
{
    bool is_positive = log_test_is_positive_path(log_data);

    fprintf(unit_test_log_file, "\nStarting %s PATH testing for %s\n\n",
        is_positive ? "POSITIVE" : "NEGATIVE", log_data->function_name);
}

void log_end_test_path(const Test_Log_Data *log_data)
{
    bool is_positive = log_test_is_positive_path(log_data);

    fprintf(unit_test_log_file, "\nEnding %s PATH testing for %s, Path %s\n",
        is_positive ? "POSITIVE" : "NEGATIVE", log_data->function_name,
        log_data->status ? "PASSED" : "FAILED");

    if (!is_positive)
    {
        fflush(unit_test_log_file);        // Current unit test is done flush the output.
    }
}

void log_generic_message(const char* log_message)
{
    fprintf(unit_test_log_file, log_message);
}

void init_test_log_data(Test_Log_Data* log_data, const char *function_name, const bool status, char *path, bool stand_alone)
{
    log_data->function_name = function_name;
    log_data->status = status;
    log_data->path = path;
    log_data->stand_alone = stand_alone;
}

Test_Log_Data *create_and_init_test_log_data(const char* function_name, const bool status, char* path, const bool stand_alone)
{
    Test_Log_Data* log_data = calloc(1, sizeof(*log_data));
    if (log_data)
    {
        init_test_log_data(log_data, function_name, status, path, stand_alone);
    }
    else
    {
        fprintf(error_out_file, "In %s calloc() failed\n", "create_and_init_test_log_data");
    }

    return log_data;
}

// provides common error report for memory allocation error.
void report_create_and_init_test_log_data_memory_failure(const char *function_name)
{
    fprintf(error_out_file, "In function %s, Memory allocation failed in create_and_init_test_log_data\n", function_name);
}

Update 09.09.2020.

Als Antwort auf die ursprüngliche Antwort von @ chux-ReinstateMonica sowie deren Kommentar unten beginnen error_reporting.hnun ERH_error_reporting.halle von diesem Modul bereitgestellten globalen Symbole mit ERH_.

lexical_analyzer.hwurde umbenannt LAH_lexical_analyzer.hund alle vom lexikalischen Analysator bereitgestellten globalen Symbole beginnen jetzt mit LAH_.

my_strdup.hwurde umbenannt SSF_safe_string_functions.hund alle Symbole beginnen nun mit SSF_, zusätzliche Funktionen wie char* SSF_strcat(char* destination, char* source, size_t destination_size);wurden hinzugefügt.

unit_test_logging.hwurde UTL_unit_test_logging.hmit den entsprechenden Namensänderungen an den Strukturen, Funktionen und der neuen Aufzählung umbenannt, die die char* pathVariable in der Struktur ersetzt.

Ähnliche Namensänderungen wurden auch an mindestens 3 anderen Dateien vorgenommen.

Als Reaktion auf die Antwort von @ G.Sliepen 2 variadische Funktion hinzugefügt worden, void UTL_va_log_fprintf(const char* format, ...);in UTL_unit_test_logging.hund void ERH_va_report_error_fprintf(const char* format, ...);in ERH_error_reporting.hder Nutzung von zu reduzieren sprintf()und die verbleibenden sprintf()Aussagen wurden umgewandelt snprintf().

Die Programme hängen nicht mehr von a BUFSIZab, sondern stdio.h ERH_error_reporting.hliefern die Konstante ERH_ERROR_BUFFER_SIZE.