/** * dll-utest.c * Unit testing of doubly-linked lists. */ // +---------+-------------------------------------------------------- // | Headers | // +---------+ #include #include #include // For strcmp #include "dll.h" // What we're testing #include "utest.h" // Unit testing utilities #include "mem.h" // For testing memory // +-----------+------------------------------------------------------ // | Constants | // +-----------+ #define BUFLEN 32 // +--------+--------------------------------------------------------- // | Macros | // +--------+ /** * Check whether a command to modify DLL gives the same values * as are in the array STRINGS. */ #define CHECK_LIST(COMMAND,DLL,STRINGS,SIZE) \ check_list (#COMMAND, COMMAND, DLL, STRINGS, SIZE) // +---------+-------------------------------------------------------- // | Helpers | // +---------+ /** * print_strings(strings,size) * Print an array of strings. */ void print_strings (char *strings[], int size, FILE *fp) { if (size == 0) { fprintf (fp, "{}"); } else { int i; fprintf (fp, "{\"%s\"", strings[0]); for (i = 1; i < size; i++) fprintf (fp, ",\"%s\"", strings[i]); fprintf (fp, "}"); } } // print_strings /** * Check whether mem memory is allocated. */ void check_mem (int expected) { int allocated = mem_allocated (); ++tests; if (expected != allocated) { ++errors; fprintf (stderr, "Expected %d bytes allocated, but %d bytes are allocated.\n", expected, allocated); } } // check_mem /** * Check the result of a list operation. Ensures that the operation * returned 1 for success and that the list matches expected_contents. */ void check_list (char *command, int result, struct dll *lst, char *expected_contents[], int expected_size) { ++tests; if (result == 0) { ++errors; fprintf (stderr, "ERROR - COMMAND FAILED\n"); fprintf (stderr, " Command: %s\n", command); fprintf (stderr, " Error: %s\n", dll_error); } else if (! dll_check (lst, expected_contents, expected_size)) { ++errors; fprintf (stderr, "ERROR\n"); fprintf (stderr, " Command: %s\n", command); fprintf (stderr, " Expected list: "); print_strings (expected_contents, expected_size, stderr); fprintf (stderr, "\n"); fprintf (stderr, " Result list: "); dll_print (lst, stderr); fprintf (stderr, "\n"); } } // check_list // +------+----------------------------------------------------------- // | Main | // +------+ int main () { struct dll *lst1; // A list to explore struct dll *lst2; // Another list to explore int initial_alloc; // The initial amount of memory allocated int two_empty_lists; // How much we have after allocated two empty lists. char *str; // A string char buffer[BUFLEN]; // A string buffer // Make sure the buffer is null terminated. (In the future, we'll // use strncpy (buffer, source, BUFLEN-1) to ensure that we never // overwrite the null.) buffer[BUFLEN-1] = '\0'; // Determine how much memory is currently allocated. initial_alloc = mem_allocated(); // Make sure that we can create empty lists lst2 = dll_new (); lst1 = dll_new (); CHECK_INT (dll_is_empty (lst1), 1); CHECK_INT (dll_is_empty (lst2), 1); // Determine how much memory is current allocated. two_empty_lists = mem_allocated(); // Append a few elements CHECK_LIST (dll_append (lst1, "a"), lst1, ((char *[]) {"a"}), 1); CHECK_LIST (dll_append (lst1, "b"), lst1, ((char *[]) {"a","b"}), 2); CHECK_LIST (dll_append (lst1, "b"), lst1, ((char *[]) {"a","b","b"}), 3); CHECK_LIST (dll_append (lst1, "c"), lst1, ((char *[]) {"a","b","b","c"}), 4); // Make sure that we can get the first and last elements CHECK_INT (dll_to_head (lst1), 1); CHECK_INT (dll_at_head (lst1), 1); CHECK_STR (dll_get (lst1), "a"); CHECK_INT (dll_to_tail (lst1), 1); CHECK_INT (dll_at_tail (lst1), 1); CHECK_STR (dll_get (lst1), "c"); // Try iterating the list from front to back CHECK_INT (dll_to_head (lst1), 1); CHECK_INT (dll_at_head (lst1), 1); CHECK_INT (dll_at_tail (lst1), 0); CHECK_STR (dll_get (lst1), "a"); CHECK_INT (dll_advance (lst1), 1); CHECK_INT (dll_at_head (lst1), 0); CHECK_INT (dll_at_tail (lst1), 0); CHECK_STR (dll_get (lst1), "b"); CHECK_INT (dll_advance (lst1), 1); CHECK_INT (dll_at_head (lst1), 0); CHECK_INT (dll_at_tail (lst1), 0); CHECK_STR (dll_get (lst1), "b"); CHECK_INT (dll_advance (lst1), 1); CHECK_INT (dll_at_head (lst1), 0); CHECK_INT (dll_at_tail (lst1), 1); CHECK_STR (dll_get (lst1), "c"); // Try iterating the list from back to front CHECK_INT (dll_to_tail (lst1), 1); CHECK_INT (dll_at_head (lst1), 0); CHECK_INT (dll_at_tail (lst1), 1); CHECK_STR (dll_get (lst1), "c"); CHECK_INT (dll_retreat (lst1), 1); CHECK_INT (dll_at_head (lst1), 0); CHECK_INT (dll_at_tail (lst1), 0); CHECK_STR (dll_get (lst1), "b"); CHECK_INT (dll_retreat (lst1), 1); CHECK_INT (dll_at_head (lst1), 0); CHECK_INT (dll_at_tail (lst1), 0); CHECK_STR (dll_get (lst1), "b"); CHECK_INT (dll_retreat (lst1), 1); CHECK_INT (dll_at_head (lst1), 1); CHECK_INT (dll_at_tail (lst1), 0); CHECK_STR (dll_get (lst1), "a"); // Clear the lists dll_clear (lst1); dll_clear (lst2); CHECK_INT (dll_is_empty (lst1), 1); CHECK_INT (dll_is_empty (lst2), 1); check_mem (two_empty_lists); // Try adding a few more elements, this time using a string variable str = "a"; CHECK_LIST (dll_append (lst1, str), lst1, ((char *[]) {"a"}), 1); str = "b"; CHECK_LIST (dll_append (lst1, str), lst1, ((char *[]) {"a", "b"}), 2); dll_clear (lst1); CHECK_INT (dll_is_empty (lst1), 1); check_mem (two_empty_lists); // Try adding a few more elements, this time using a string variable strncpy (buffer, "alpha", BUFLEN-1); CHECK_LIST (dll_append (lst1, buffer), lst1, ((char *[]) {"alpha"}), 1); strncpy (buffer, "beta", BUFLEN-1); CHECK_LIST (dll_append (lst1, buffer), lst2, ((char *[]) {"alpha", "beta"}), 2); strncpy (buffer, "gamma", BUFLEN-1); CHECK_LIST (dll_append (lst1, buffer), lst2, ((char *[]) {"alpha", "beta", "gamma"}), 3); dll_clear (lst1); CHECK_INT (dll_is_empty (lst1), 1); check_mem (two_empty_lists); // Append a few elements CHECK_LIST (dll_append (lst1, "a"), lst1, ((char *[]) {"a"}), 1); CHECK_LIST (dll_append (lst1, "b"), lst1, ((char *[]) {"a","b"}), 2); CHECK_LIST (dll_append (lst1, "c"), lst1, ((char *[]) {"a","b","c"}), 3); CHECK_LIST (dll_append (lst1, "d"), lst1, ((char *[]) {"a","b","c","d"}), 4); // Delete a middle element CHECK_INT (dll_to_head (lst1), 1); CHECK_INT (dll_advance (lst1), 1); CHECK_LIST (dll_delete (lst1), lst1, ((char *[]) {"a","c","d"}), 3); CHECK_STR (dll_get (lst1), "c"); // Delete the first element CHECK_INT (dll_to_head (lst1), 1); CHECK_LIST (dll_delete (lst1), lst1, ((char *[]) {"c","d"}), 2); CHECK_STR (dll_get (lst1), "c"); // Delete the last element CHECK_INT (dll_to_tail (lst1), 1); CHECK_LIST (dll_delete (lst1), lst1, ((char *[]) {"c"}), 1); // Append, because deleting something screws stuff up CHECK_LIST (dll_append (lst1, "e"), lst1, ((char *[]) {"c","e"}), 2); // Clear the two things remaining CHECK_INT (dll_to_head (lst1), 1); CHECK_LIST (dll_delete (lst1), lst1, ((char *[]) {"d"}), 1); CHECK_LIST (dll_delete (lst1), lst1, ((char *[]) {}), 0); check_mem (two_empty_lists); // Prepend a few elements CHECK_LIST (dll_prepend (lst1, "z"), lst1, ((char *[]) {"z"}), 1); CHECK_LIST (dll_prepend (lst1, "y"), lst1, ((char *[]) {"y","z"}), 2); CHECK_LIST (dll_prepend (lst1, "x"), lst1, ((char *[]) {"x","y","z"}), 3); CHECK_LIST (dll_prepend (lst1, "w"), lst1, ((char *[]) {"w","x","y","z"}), 4); // Check insert_before and insert_after at the head CHECK_INT (dll_to_head (lst1), 1); CHECK_LIST (dll_insert_before (lst1, "0"), lst1, ((char *[]) {"0","w","x","y","z"}), 5); CHECK_LIST (dll_insert_after (lst1, "1"), lst1, ((char *[]) {"0","w","1","x","y","z"}), 6); // Check insert_before and insert_after at the tail // Check insert_before and insert_after at other locations in the list // Remove the lists dll_free (lst1); dll_free (lst2); check_mem (initial_alloc); // Print some messages fprintf (stderr, "%d tests conducted.\n", tests); fprintf (stderr, "%d errors encountered.\n", errors); // And we're done return errors; } // main