Search code examples
c++linuxtestingfgetscstdio

How do I intentionally trigger an error in fgets()?


Summary

When using fgets(), I have error checking code that performs some remediation in case fgets() returns null but isn't yet at end of file. I'd like to exercise this section of code to verify it's working as intended.

Is there a canonical way to trigger fgets() to fail? Either through manual means (somehow deleting the file between calls to fgets()), some test setup (providing a knowingly "corrupt" file), or something else?


Minimally Reproducible Example

fgets_fail_test.cpp:

// Test code to show minimally reproducible example of fgets() error handling
// The desire is to manually trigger a failure in fgets()

#include <errno.h>
#include <iostream>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
    FILE *fp = fopen("test_file.txt", "r"); //assume this works, not testing this
    char buf[100]; //assume large enough to hold data
    unsigned int lineno = 1;

    while (fgets(buf, 100, fp)) {
        std::cout << buf;
        lineno++;
    }

    if (feof(fp)) {
        std::cout << "End of file encountered.\n";
    }
    else if (ferror(fp)) { // how do I trigger an fgets error to cause this to return true?
        printf("Encountered read error at line no %u. Error: %d, %s.\n",
               lineno,
               errno,
               strerror(errno));
    }

    fclose(fp); // assume this works, not testing this

    return 0;
}

test_file.txt:

ABCDEFG
HIJKLMN
OPQRSTU
VWXYZ
The quick brown fox
jumped over the
lazy dog.
Goodbye.

Theories

While I could simply replace the code in question with some test scaffolding (write an fgets wrapper that keeps internal state, increment a counter, once it reaches line #N, it returns null and manually sets file error and errno) I feel like there should be some "builtin" way to do this?

If the only solution is scaffolding, I'll do that. Honestly maybe I'm trying to be too clever here.


Solution

  • So many good answers were given in some related and linked questions, which may make my question a duplicate. Although I was focusing on fgets() when I did my initial search, so I wasn't able to find these.

    Some of the relevant questions are:

    Because my program reads data from a list of known files with a given filename format, I needed to use a method that allowed me to provide a faulty file to the program (without having to modify code). I decided to use this answer to do so.

    Method

    To generate a faulty file that would trigger a read failure for fgets(), I took advantage of /proc/self/mem's "feature" that reading from it will cause an I/O error.

    I deleted my old input file, and linked a new one under the old name to /proc/self/mem:

    ln -s /proc/self/mem test_file.txt
    

    Running the file then produced the following output:

    Encountered read error at line no 1. Error: 5, Input/output error.
    

    Indicating that we triggered the error. Thanks to @Artyer's comment where they originally linked me to the related questions.