Back to home page

Enduro/X

 
 

    


0001 #include <cgreen/constraint.h>
0002 #include <cgreen/message_formatting.h>
0003 #include <cgreen/string_comparison.h>
0004 #include <inttypes.h>
0005 #ifndef __cplusplus
0006 #include <stdbool.h>
0007 #endif
0008 #include <stdio.h>
0009 #include <stdlib.h>
0010 #include <string.h>
0011 
0012 #ifdef _MSC_VER
0013 #include "wincompat.h"
0014 #endif
0015 
0016 #ifdef __cplusplus
0017 namespace cgreen {
0018 #endif
0019 
0020 static int find_index_of_difference(char *expected, char *actual, size_t size_to_compare) {
0021     char *expectedp = expected;
0022     char *actualp = actual;
0023 
0024     while (size_to_compare--) {
0025         if (*expectedp++ != *actualp++) {
0026             return (int)(actualp - actual);
0027         }
0028     }
0029 
0030     return -1;
0031 }
0032 
0033 static bool actual_value_not_necessary_for(Constraint *constraint, const char *actual_string, const char *actual_value_string) {
0034     (void)constraint; /* UNUSED! */
0035     return strings_are_equal(actual_string, actual_value_string) ||
0036             strings_are_equal(actual_string, "true") ||
0037             strings_are_equal(actual_string, "false");
0038 }
0039 
0040 
0041 bool parameters_are_not_valid_for(Constraint *constraint, intptr_t actual) {
0042     char *message = validation_failure_message_for(constraint, actual);
0043 
0044     bool not_valid = (strlen(message) > 0);
0045 
0046     free(message);
0047 
0048     return not_valid;
0049 }
0050 
0051 char *validation_failure_message_for(Constraint *constraint, intptr_t actual) {
0052     const char *name_has_incorrect_size_message =
0053             "\t\tWanted to compare contents with [%s], but [%lu] was given for the comparison size.";
0054     const char *null_used_for_compare_message =
0055             "\t\tWanted to compare contents with [%s], but NULL was used for the pointer we wanted to compare to."
0056             "\t\tIf you want to explicitly check for null, use the is_null constraint instead.";
0057     const char *null_used_for_actual_message =
0058             "\t\tWanted to compare contents of [%s] but it had a value of NULL.\n"
0059             "\t\tIf you want to explicitly check for null, use the is_null constraint instead.";
0060 
0061 
0062 
0063     size_t message_size = strlen(name_has_incorrect_size_message) +
0064             strlen(null_used_for_compare_message) +
0065             strlen(null_used_for_actual_message) +
0066             512; /* just in case */
0067 
0068     char *message = (char *)malloc(message_size);
0069     memset(message, 0, message_size);
0070 
0071     if (is_content_comparing(constraint)) {
0072         const char *compared_to_name;
0073         if (constraint->parameter_name != NULL) {
0074             compared_to_name = constraint->parameter_name;
0075         } else {
0076             compared_to_name = constraint->expected_value_name;
0077         }
0078 
0079         if (constraint->size_of_expected_value <= 0) {
0080             snprintf(message + strlen(message), message_size - strlen(message) - 1,
0081                     name_has_incorrect_size_message,
0082                     compared_to_name,
0083                     (unsigned long)constraint->size_of_expected_value);
0084 
0085             return message;
0086         }
0087 
0088         if ((void *)actual == NULL) {
0089             snprintf(message + strlen(message), message_size - strlen(message) - 1,
0090                     null_used_for_compare_message,
0091                     compared_to_name);
0092 
0093                 return message;
0094         }
0095 
0096         if ((void *)constraint->expected_value == NULL) {
0097             snprintf(message + strlen(message), message_size - strlen(message) - 1,
0098                     null_used_for_actual_message,
0099                     compared_to_name);
0100 
0101                 return message;
0102         }
0103     }
0104 
0105     return message;
0106 }
0107 
0108 
0109 static char *next_percent_sign(const char *s) {
0110     return strchr(s, '%');
0111 }
0112 
0113 size_t count_percent_signs(char const *s)
0114 {
0115     size_t count = 0;
0116     char const *p = next_percent_sign(s);
0117     while (p != NULL) {
0118         count++;
0119         p = next_percent_sign(p+1);
0120     }
0121     return count;
0122 }
0123 
0124 
0125 char *copy_while_doubling_percent_signs(char *string, char const *original)
0126 {
0127     char *destination = string;
0128     const char *source = original;
0129     const char *next = next_percent_sign(source);
0130     for (; next != NULL; next = next_percent_sign(next+1)) {
0131         size_t len = next - source + 1;
0132         memcpy(destination, source, len);
0133         destination += len;
0134         *destination++ = '%';
0135         source = next+1;
0136     }
0137     strcpy(destination, source);
0138 
0139     return string;
0140 }
0141 
0142 
0143 void double_all_percent_signs_in(char **original)
0144 {
0145     size_t percent_count = count_percent_signs(*original);
0146     if (percent_count == 0) {
0147         return;
0148     }
0149     char *new_string = (char *)malloc(strlen(*original) + percent_count + 1);
0150     if (new_string == NULL) {
0151         return;
0152     }
0153     copy_while_doubling_percent_signs(new_string, *original);
0154     free(*original);
0155     *original = new_string;
0156 }
0157 
0158 
0159 char *failure_message_for(Constraint *constraint, const char *actual_string, intptr_t actual) {
0160     char actual_value_string[32];
0161     const char *actual_value_constraint_name = "Expected [%s] to [%s]";
0162     const char *expected_value_name =  "[%s]\n";
0163     const char *actual_value_as_string = "\t\tactual value:\t\t\t[\"%s\"]";
0164     const char *at_offset = "\t\tat offset:\t\t\t[%d]";
0165     const char *actual_value_message = "\t\tactual value:\t\t\t[%" PRIdPTR "]";
0166     char *message;
0167     size_t message_size = strlen(actual_value_constraint_name) +
0168             strlen(expected_value_name) +
0169             strlen(actual_value_as_string) +
0170             strlen(at_offset) +
0171             strlen(actual_value_message) +
0172             strlen(constraint->expected_value_message) +
0173             strlen(constraint->expected_value_name) +
0174             strlen(constraint->name) +
0175             strlen(actual_string) +
0176             512; /* just in case */
0177 
0178     snprintf(actual_value_string, sizeof(actual_value_string) - 1, "%" PRIdPTR, actual);
0179 
0180     if (values_are_strings_in(constraint)) {
0181         message_size += strlen((char *)constraint->expected_value);
0182         if (actual != (intptr_t)NULL) {
0183             message_size += strlen((char *)actual);
0184         }
0185     }
0186 
0187     message = (char *)malloc(message_size);
0188 
0189     snprintf(message, message_size - 1,
0190             actual_value_constraint_name,
0191             actual_string,
0192             constraint->name);
0193 
0194     if (no_expected_value_in(constraint)) {
0195         return message;
0196     } else
0197         strcat(message, " ");
0198 
0199     snprintf(message + strlen(message), message_size - strlen(message) - 1,
0200              expected_value_name,
0201              constraint->expected_value_name);
0202 
0203     if (actual_value_not_necessary_for(constraint, actual_string, actual_value_string)) {
0204         /* when the actual string and the actual value are the same, don't print both of them */
0205         /* also, don't print "0" for false and "1" for true */
0206         /* also, don't print expected/actual for contents constraints since that is useless */
0207         return message;
0208     }
0209 
0210     /* for string constraints, print out the strings encountered and not their pointer values */
0211     if (values_are_strings_in(constraint)) {
0212         snprintf(message + strlen(message), message_size - strlen(message) - 1,
0213                  actual_value_as_string,
0214                  (const char *)actual);
0215         if (strstr(constraint->name, "not ") == NULL || strstr(constraint->name, "contain ") != NULL) {
0216             strcat(message, "\n");
0217             snprintf(message + strlen(message), message_size - strlen(message) - 1,
0218                      constraint->expected_value_message,
0219                      (const char *)constraint->expected_value);
0220         }
0221         /* The final string may have percent characters, so, since it is
0222            later used in a (v)printf, we have to double them
0223         */
0224         double_all_percent_signs_in(&message);
0225         return message;
0226     }
0227 
0228     if (is_content_comparing(constraint)) {
0229         int difference_index = find_index_of_difference((char *)constraint->expected_value,
0230                                                         (char *)actual, constraint->size_of_expected_value);
0231         snprintf(message + strlen(message), message_size - strlen(message) - 1,
0232                  at_offset,
0233                  difference_index);
0234         return message;
0235     }
0236 
0237     snprintf(message + strlen(message), message_size - strlen(message) - 1,
0238              actual_value_message,
0239              actual);
0240 
0241     if (strstr(constraint->name, "not ") == NULL) {
0242         strcat(message, "\n");
0243         snprintf(message + strlen(message), message_size - strlen(message) - 1,
0244                  constraint->expected_value_message,
0245                  constraint->expected_value);
0246     }
0247 
0248     return message;
0249 }
0250 
0251 #ifdef __cplusplus
0252 }
0253 #endif