Codice comune di unit test - Follow-up

Sep 02 2020

Questa domanda è una domanda di follow-up alla parte del codice comune di unit test delle mie domande sull'analizzatore lessicale.

La mia preoccupazione principale è il codice nel file di intestazione e il file sorgente C che implementa strdup (). Poiché il programma di cui fa parte questo codice è progettato per essere multipiattaforma, deve essere compilato ed eseguito su Windows o Linux e dovrebbe essere compatibile con entrambi. La strdup()funzione fa parte dello standard C2X C quindi se diventa disponibile il codice dovrebbe continuare a compilarsi e funzionare. Le #define nel file di intestazione si basano sulla gccversione di string.h.

Una preoccupazione secondaria è la prestazione, molti dei parametri sono cambiati in const. I membri della struttura Test_Log_Data sono stati riordinati per migliorare l'utilizzo della memoria.

Una terza preoccupazione era l'uso arcaico, l'extern che precede i prototipi di funzione è stato rimosso in tutti i file di intestazione, non solo common_unit_test_logic.h.

Il codice originale è fornito per il confronto.

Nuovo codice

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

Codice originale :

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

Risposte

2 chux-ReinstateMonica Sep 08 2020 at 22:55

mystrdup()ha un difetto: per essere * nix-like, mi aspetto di rilevare i casi che potrebbero essere impostati errno.

IMO, usa malloc()e copia anche il carattere null .

Da Quando è una buona idea usare strdup (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

I file common_unit_test_logic.*sono troppo complessi.

Il file common_unit_test_logic .c e il file di intestazione non seguono il principio di responsabilità unica che afferma

… Che ogni modulo, classe o funzione dovrebbe avere la responsabilità su una singola parte della funzionalità fornita dal software, e tale responsabilità dovrebbe essere interamente incapsulata da quel modulo, classe o funzione.

Ciò ha forzato le istruzioni #ifdef e #ifndef non necessarie nel codice. Questo è stato rettificato da rottura common_unit_test_logic.ce common_unit_test_logic.hin 3 moduli separati, error_reporting, my_strdup, e unit_test_logging.

Solo il unit_test_loggingmodulo è ancora nella Common_UnitTest_Codedirectory sotto la UnitTestsdirectory. Il error_reportingmodulo e il my_strdupmodulo sono stati entrambi spostati nella VMWithEditordirectory del codice sorgente in modo che possano essere condivisi con il progetto principale e con i progetti di più unit test. Un quarto modulo è default_programstato creato anche per il programma principale e alcuni degli altri test unitari, il codice è stato ifdef'de fuori dal test unitario dell'analizzatore lessicale.

La suddivisione del codice consente un maggiore riutilizzo di ciascuno dei moduli, ma richiede #includeistruzioni aggiuntive in molti dei file.

I moduli separati:

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

programma_predefinito.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);
}

Aggiorna 9/9/2020.

In risposta alla risposta originale di @ chux-ReinstateMonica e al loro commento di seguito, error_reporting.hora ERH_error_reporting.htutti i simboli globali forniti da quel modulo iniziano con ERH_.

lexical_analyzer.hè stato rinominato LAH_lexical_analyzer.he tutti i simboli globali forniti dall'analizzatore lessicale ora iniziano con LAH_.

my_strdup.hè stato rinominato SSF_safe_string_functions.he tutti i simboli ora iniziano con SSF_, char* SSF_strcat(char* destination, char* source, size_t destination_size);sono state aggiunte funzioni aggiuntive come .

unit_test_logging.hè stato rinominato UTL_unit_test_logging.hcon le modifiche di nome corrispondenti alle strutture, alle funzioni e alla nuova enum che sostituisce la char* pathvariabile nella struttura.

Sono state apportate modifiche al nome simili anche ad almeno altri 3 file.

In risposta alla risposta di @ G.Sliepen sono state aggiunte 2 funzioni variadiche, void UTL_va_log_fprintf(const char* format, ...);in UTL_unit_test_logging.he void ERH_va_report_error_fprintf(const char* format, ...);in ERH_error_reporting.hper ridurre l'utilizzo di sprintf()ed eventuali sprintf()istruzioni rimanenti sono state convertite in snprintf().

I programmi non dipendono più da una BUFSIZdi stdio.h ERH_error_reporting.hfornisce la costante ERH_ERROR_BUFFER_SIZE.