Advanced C Programming

Spring 2019 :: ECE 264 :: Purdue University

⚠ This is for Spring 2019, not the current semester.
Due 4/15

Testing BMP

Learning goals

You should learn:

  1. Unit testing
  2. Binary files (reinforce)
  3. Image file formats (reinforce)
  4. Structures (reinforce)

Overview

Since HW05, using miniunit.h has been recommended but optional. We left it that way because we wanted you to be able to focus on one thing at a time. With this assignment, you will create a test suite using miniunit.h for HW11.

This assignments gives you a week to solidify what you have already learned about image files and unit testing. You do not need to create any new implementation code. This homework is all about the testing.

Most of this homework will be creating test_▒▒▒(…) functions. In addition, you will create a few helper functions that make it possible to test your HW11 without any preexisting image files.

Your test_bmp.c will use the interface of miniunit_h (e.g., mu_start(…), mu_check(…), mu_end(…), and mu_run(…)) but we will test with our own miniunit.h. That way, if you have customized the messages or anything else, you don't have to worry about inconsistencies.

We ask you to submit your miniunit.h only to aid in case of problems. We do not plan to use your miniunit.h.

Starter code

There is no starter code.

Getting Started

Create a new directory for HW12 and copy in your bmp.c and bmp.h from HW11.

Start by creating one very simple test. Initially, you may copy the 6x6_24bit.bmp file into your directory and use that to test.

By the time you are finished, your tests should run without that file present.

Requirements

  1. Your submission must contain each of the following file, as specified:
    file contents
    test_bmp.c main function
    main(int argc, char✶ argv[])
    return type: int
    Run all of your test functions.
    1. Body of main(…) should look like this:
      mu_run(test_read); mu_run(test_write); ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ … return EXIT_SUCCESS;
    2. Do not include any printf(…) or fprintf(…) statements, or anything else that would print directly to the console..
    3. This must result in 100% code line coverage.
    test functions
    test write()
    return type: int
    Create a file using create_bmp(…) and then write it using write_bmp(…).
    1. Free the image before this function returns.
    test read()
    return type: int
    Read a file and then free it.
    1. Before you can read the file, you must first write a file using create_bmp(…) and write_bmp(…).
    2. Free the image before this function returns.
    test ▒▒▒▒()
    return type: int
    Additional test_▒▒▒▒(…) test functions, as needed to test your HW11 adequately.
    • Each function should look like this:
      int test_▒▒▒▒() { mu_start(); ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ mu_end(); }
    support functions
    create bmp(size t w, size t h, uint8 t r, uint8 t g, uint8 t b)
    return type: BMPImage✶
    Create a new BMPImage object on the heap.
    1. Return a new image (BMPImage object) with the specified width and height (in pixels), all set to the specified color.
    2. Caller of create_bmp(…) is responsible for calling free_bmp(…).
    3. You may ignore the possibility of allocation failures in this function.
    set pixel(BMPImage✶ img, size t x, size t y, uint8 t r, uint8 t g, uint8 t b)
    return type: void
    Set the color of the image at the specified location to the specified color.
    1. You may assume the image contains a pixel at the specified x and y coordinates.
  2. The above functions will depend on everything in HW11 (e.g., read_bmp(…), write_bmp(…), crop_bmp(…), and free_bmp(…)). Requirements for bmp.c are as specified in HW11.
  3. Only the following externally defined functions and constants are allowed in your .c files.
    header functions/symbols allowed in…
    errno.h errno test_bmp.c
    assert.h assert(…) test_bmp.c
    string.h strcmp(…) test_bmp.c
    stdbool.h true, false test_bmp.c
    stdlib.h EXIT_SUCCESS, malloc, free, NULL, EXIT_FAILURE test_bmp.c
    stdio.h clearerr(…), fopen, fclose, fwrite, ferror test_bmp.c
  4. Your final submission must not rely on any preexisting image files.
  5. You may add additional helper functions, as you see fit.
  6. No function may be >50 lines long.
  7. We should be able to use your test_bmp.c to test any bmp.c—not just yours. (See Q&A #2 and #3.)
  8. Submissions must meet the code quality standards and the course policies on homework and academic integrity.

Submit

To submit HW12, type 264submit HW12 test_bmp.c miniunit.h clog.h bmp.c from inside your hw12 directory. We do not expect to use your miniunit.h, clog.h, or bmp.c to test your test_bmp.c. They are submitted only as a failsafe in case of unforeseen problems.

Q&A

  1. What should test_▒▒▒(…) return?
    They return the line number of the first error detected, or 0 if no error was found. This is as specified in HW05. You don't need to do anything new for HW12 regarding that.
  2. How can I ensure that my test_bmp.c will work with any other bmp.c?
    As long as you are following the public interface of HW11 (i.e., read_bmp(…), write_bmp(…), etc.), it shouldn't matter.
    Note: This has been the expectation for all test_▒▒▒.c throughout the semester. It was explicitly stated in HW04, requirement #5.
  3. What if I followed the public interface, but my test_bmp.c didn't work with some other bmp.c you tested with (i.e., for reasons that weren't my fault)?
    Submit a regrade request. We'll try to be reasonable. We expect this to be rare, but it might conceivably happen, especially if you were checking for specific error message text.
  4. Why is errno allowed, but not strerror(…)?
    You shouldn't need to produce any error messages using strerror(…)directly in test_bmp.c, but having errno might be helpful to ensure that error conditions don't bleed from one test to the next. Actually, I doubt this will be needed, but we left errno as allowed, just in case.
  5. What is set_pixel(…) for?
    You can use it in your tests to simplify code for creating images with specific colors in specific locations. For example, if you wanted to recreate our 6x6_24bit.bmp image, the code would look something like this:
    void _make_6x6_24bit() {
        // Create a 6x6 image, initialized to all black.
        BMPImage* img = create_bmp(6, 6, 0,   0,   0);
        // BMPImage* img = create_bmp(6, 6, 255, 255, 255);  // all white
        // BMPImage* img = create_bmp(6, 6, 255, 0,   0);    // all red
        
        //             x  y  ┌r┐  ┌g┐  ┌b┐
        set_pixel(img, 0, 0, 255,   0,   0);  // (0, 0) ← red
        set_pixel(img, 1, 0, 255,   0,   0);  // (1, 0) ← red
        set_pixel(img, 2, 0,   0,   0, 255);  // (2, 0) ← blue
        // … and so on.
    }
    // This is just an illustration, but you may copy/adapt if you wish
  6. How can I create a corrupt image?
    Here's a start:
    void _corrupt_img_height(BMPImage* img, int height_delta) {
        img -> header.height += height_delta;
    }
    // This is just an illustration, but you may copy/adapt if you wish
  7. My bmp.c has bugs, and it's a mess. Any tips?

    In class, I said that ideally, a function should contain code at a given level of abstraction. For example, a function that operates at the level of pixels should not contain any byte-level calculations.

    To the extent that you can follow this, it will make problems easier to think about because a given function will read at the same level of detail you would describe verbally.

    The helper functions below (just outlines, obviously) handle byte-level calculations so that code in your crop_bmp(…) and set_pixel(…) won't be cluttered with the calculations for finding offsets and such.

    None of this is absolute. Separate levels of abstraction only when it makes your code easier to think about and read.

    int _get_offset_in_data(int x, int y, int img_w, int img_h) {
        int padding_per_row = ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒;
        int row_start_index = ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒;
        int index_in_row    = ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒;
        return ▒▒▒▒▒▒▒▒▒▒;
    }
    
    
    // _Color - a helper structure used only
    typedef struct {
        uint8_t r;
        uint8_t g;
        uint8_t b;
    } _Color;
    
    
    _Color _get_pixel(const BMPImage* image, int x, int y) {
        ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒;
    
        return (_Color) { .r = ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒,
                          .g = ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒,
                          .b = ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ };
    }
    
    // This is just an illustration, but you may copy/adapt if you wish
  8. How can I ensure that my tests work with someone else's code if I tests for the text of my specific error messages?
    Try to formulate your tests in such a way that they don't depend on that. However, we won't hold you to this. If your tests fail due to not matching error message text or anything else that was not tightly specified in HW11, we will retest manually using the bmp.c you submitted. However, that may require a stop by the instructor's office hours and/or a regrade request.
  9. Will there be a pretester for HW12?
    No. This assignment is about testing. The usual framework for that won't work for this one.
  10. What if my miniunit.h isn't right?
    The correctness of your miniunit.h or clog.h will not affect your score for HW12. We do not plan to use your miniunit.h or clog.h to test. We ask you to submit them, only as a failsafe.
    Example: Some people used log_green(…) and log_red(…) in their miniunit.h. That wasn't correct for HW05 since miniunit.h would only work when the program was compiled with -DDEBUG. However, it won't affect your HW12. As long as the macro names are correct (i.e., mu_start(…), mu_run(…), mu_end(…), and mu_check(…)) are correct, your test_bmp.c should work just fine with our miniunit.h.

Updates

4/9/2019 Allow fopen(…), fclose(…), fwrite(…), and ferror(…).
4/10/2019 Amended submission instructions: Submit bmp.c, miniunit.h, and clog.h, as well as test_bmp.c. Clarified that this depends on the functions in HW11. Added specifics about how to set up your directory. Added Q&A #1-4. Your test_bmp.c should work with any bmp.c.
4/13/2019 Added Q&A #5-7.
4/15/2019 Added Q&A #8-9.
Added Q&A #10.
Corrected example code to show main(…) calling test_read(…) and test_write(…) (not test_read_bmp(…) and test_read_write_bmp(…)). This was inconsequential since we will not check the contents of your main(…).