static void assert_fizz_buzz(const char * expected, int n) { char actual[16]; ... }
The effect of not initializing the array was that state from one test leaked into another test and I got an unexpectedly passing test. I fixed the problem by initializing the array.
static void assert_fizz_buzz(const char * expected, int n) { char actual[16] = ""; ... }
The most recent episode (in cyber-dojo naturally) revolved around this same issue. This time I was redoing the roman numerals exercise (1 → "I", 2 → "II", etc) in C. My custom assert function started like this.
... #define PRINT(s) print_string(#s, s) static void print_string(const char * name, const char * s) { printf("%10s: \"%s\"\n", name, s); } static void assert_to_roman(const char * expected, int n) { char actual[32]; to_roman(actual, sizeof actual, n); if (strcmp(expected, actual) != 0) { printf("to_roman(%d) FAILED\n", n); PRINT(expected); PRINT(actual); assert(false); } }
Once again I failed to initialize the array. I'm a slow learner. This time the test failed unexpectedly! And the diagnostic was even more unexpected:
to_roman(1) FAILED expected: "I" actual: "I"
This is a less than ideal diagnostic! It seemed that
strcmp
and printf
had differing opinions on what a string is!
I fixed it by adding initialization.... static void assert_to_roman(const char * expected, int n) { char actual[32] = ""; to_roman(actual, sizeof actual, n); if (strcmp(expected, actual) != 0) { printf("to_roman(%d) FAILED\n", n); PRINT(expected); PRINT(actual); assert(false); } }
After this the tests passed. This addressed the immediate problem but it did not address to the root cause. So I removed the initialization and (with a hat tip to Mr Jonathon Wakely) I reworked
print_string
to display the length of the string as well as an indication
of the (un)printability of each character:static void print_string(const char * name, const char * s) { printf("%10s: \"%s\" %d ", name, s, (int)strlen(s)); for (size_t i = 0; i != strlen(s); i++) { putchar(isprint(s[i]) ? 'P' : 'U'); } putchar('\n'); }
With this change the diagnostic became:
to_roman(1) FAILED expected: "I" 1 P actual: "I" 4 UUUP
Much better. Then I thought about initializing the array a bit more. I realized that initializing the array to the empty string was a poor choice since it masked a fault in the implementation of
to_roman
which did not start like this:void to_roman(char buffer[], size_t size, int n) { buffer[0] = '\0'; ... }
So I added that and reworked
print_string
as follows:static void assert_to_roman(const char * expected, int n) { char actual[32]; memset(actual, '!', sizeof actual); to_roman(actual, sizeof actual, n); ... }
And I was back to green :-)
No comments:
Post a Comment