3

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.

JohnFilleau
  • 3,191
  • 1
  • 13
  • 21
  • 1
    Do you have to use C file handling? If you are using C++ you can use the C++ file streams and they allow you to set and clear the error bits. – NathanOliver Jul 16 '19 at 15:55
  • 1
    Since you open in "text mode", what about special characters in the file? By special I mean `(int)charater < 32` – Ripi2 Jul 16 '19 at 15:58
  • 2
    If you are on a linux system, you can [use one of these](https://unix.stackexchange.com/questions/77492/special-file-that-causes-i-o-error). – Artyer Jul 16 '19 at 16:02
  • @NathanOliver For the purposes of this assignment, I have to use C file handling. It's legacy code that I'm actually working on. I think I'll try to upgrade to C++ file streams when I can in the future, but for this actual work item (and intellectual curiosity) C handling is required. This is a good solution, however, when C++ file handling is used. – JohnFilleau Jul 16 '19 at 16:25
  • @Ripi2 I tested this just now, and fgets() happily reads special characters without error. Unfortunately this is not a solution :( – JohnFilleau Jul 16 '19 at 16:26
  • @Artyer I think you might have given me the solution I was looking for. I'm testing some of these methods now, and I'll reply to my original post with the best one (and a relink of the link you provided) once I find the solution. – JohnFilleau Jul 16 '19 at 16:29
  • 1
    You could try doing the operation in a loop, and detaching the device mid-operation. – eerorika Jul 16 '19 at 16:30
  • @eerorika Can you say more about this? Can a "device" in this case be a single file? Or do I need to detach the entire drive or partition that the device is sitting on? I'm not ruling out any methods here! – JohnFilleau Jul 16 '19 at 16:32
  • 1
    @John I was thinking about the physical hardware. I suspect that unmounting or other virtual operations might be handled too gracefully, though some forceful options might work. – eerorika Jul 16 '19 at 16:35
  • @eerorika Unfortunately I'm at work and don't have access to the hardware, but I'm always a fan of simplest solution. And honestly, that sounds as simple as possible. When in doubt, smash. Despite not helping *me*, hopefully your suggestion will help *someone* in the future. – JohnFilleau Jul 16 '19 at 16:38

1 Answers1

1

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.

JohnFilleau
  • 3,191
  • 1
  • 13
  • 21