Alexis Wilke
Alexis Wilke

Reputation: 20818

Is there a way to generate an error in a process attempting to write to a file? (for test purposes)

As I'm writing a test, there is special case of a FILE* which I handle this way:

// first we open the file
bool FileOutput::open(std::string const& filename)
{
    if(f_file.is_open())
    {
        throw std::runtime_error("already open");
    }

    f_file.open(filename.c_str());
    if(!f_file.is_open())
    {
        return false;
    }

    return true;
}

// later we write to it
void FileOutput::internal_write(std::string const& data)
{
    f_file << data;
    if(!f_file)
    {
        throw std::runtime_error("I/O error: could not write to output.");
    }
}

As I'm writing my test, I would like to induce an I/O error in internal_write(). In other words, I was the << operator (and whatever underlying I/O functions) to generate an error so that !f_file becomes true.

This is for a test to make sure that errors to indeed end up in throwing. So I'm not looking at writing the code differently.

Note that closing the file is not a good idea and the f_file is not accessible form the outside and there are no close() functions (it closes when the object gets destroyed.)

I looked into locks, but it does not look like that would work. It would just block here without timeout while a thread blocks the file for a little while. What else could be done?

Upvotes: 0

Views: 63

Answers (1)

user184968
user184968

Reputation:

Note that closing the file is not a good idea and the f_file is not accessible form the outside and there are no close() functions (it closes when the object gets destroyed.)

Is closing bad just because f_file is not accessible? If so then write a shell script that while your test program is running gets PID of the test program and closes a required file handle.

The scripts does two steps:

1) Run lsof to get file handle of your file

lsof -p PID | grep yourfile | awk '{print $4}' | tr -d wru

In the 4th field there will be the number of the file handle for your file. Assing it to the environment variable HANDLE_ID_FROM_LSOF.

2) Run gdb to close the handle you need to become bad

gdb --batch-silent your-program -p PID -ex "call close($HANDLE_ID_FROM_LSOF)" -ex "detach"

After running gdb the file handle will be closed and hopefully you will get an error you need. At least with strace it looks like this:

write(1, "10606216\n10606217\n10606218\n10606"..., 65536) = 65536
write(1, "7\n10613498\n10613499\n10613500\n106"..., 65536) = 65536
write(1, "779\n10620780\n10620781\n10620782\n1"..., 65536) = 65536
write(1, "\n15827311\n15827312\n15827313\n1582"..., 65536) = -1 EBADF (Bad file descriptor)
write(1, "\n15834593\n15834594\n15834595\n1583"..., 65536) = -1 EBADF (Bad file descriptor)
write(1, "\n15841875\n15841876\n15841877\n1584"..., 65536) = -1 EBADF (Bad file descriptor)

This is an example of this script:

$ cat close_file.sh
#!/bin/sh

TARGET_PROG=$1
TARGET_PID=$2
TARGET_FILE=$3
TARGET_FILE_FD=$(lsof -p $TARGET_PID | grep seq.txt | awk '{print $4}' |  tr -d wur)
gdb --batch-silent $TARGET_PROG  -p $TARGET_PID -ex "call close($TARGET_FILE_FD)" -ex "detach"

The first parameter - the full path to your program, the second parameter - the process ID, the third parameter -- the name of the file that needs to be closed.

This example shows how this script can be run. First run your test program. Then run the script. This is an example of running it:

$ ./close_file.sh /usr/bin/seq 16640 seq.txt

Upvotes: 1

Related Questions