Question

I am working on updating unit tests for my core library, and came across a scenario regarding filelocks (flock), and wanted to see how others implemented these types of unit tests.

Basically I have a utlity File class which will write contents to a file:

class CoreLib_Api_File_Package
{
    public static function write(array $options)
    {
        ...

        if (!$file->flock(LOCK_EX)) {
            throw new CoreLib_Api_Exception('Unable to obtain lock on file');
        }

        ...
    }
}

And my unit test looks like:

public function testWriteException_UnableToSecureLock()
{
    $this->touchFile($this->destFileUri);

    $file = new SplFileObject($this->destFileUri, CoreLib_Api_File::MODE_WRITE);

    $file->flock(LOCK_EX);

    CoreLib_Api_File_Package::write(array('fileUri' => $this->destFileUri, 'content' => $this->testContent, 'mode' => CoreLib_Api_File::MODE_WRITE));
}

As you can see from the test code I am putting an explicit lock on $this->destFileUri before I make the API call to the write() method. What I would expect is that the exception throw new CoreLib_Api_Exception('Unable to obtain lock on file'); to be thrown. Instead, my unit test just hangs indefinitely.

What would be the proper way to test whether a file has a lock?

Thanks in advance.

Was it helpful?

Solution

You should check this comment on the flock docs page.

Essentially, flock will pause execution until it is able to obtain the lock. Take the following code, which is similar to your situation above:

$file = 'file.txt';

$first = fopen($file, "w");
flock($first, LOCK_EX);

$second = fopen($file, "w");

# Your script will pause on the next line until timeout
# due to the inability to obtain a lock.
flock($second, LOCK_EX);

However, you can obtain non-blocking locks like this:

flock($file, LOCK_EX | LOCK_NB);

I am not sure if there are negative implications to locking files this way. Also, while the docs say LOCK_NB does not work on Windows, I just tested it and it worked on my machine. So, you could do something like this in your CoreLib_Api_File_Package::write method:

class CoreLib_Api_File_Package
{
    public static function write(array $options)
    {
        if ( ! $file->flock(LOCK_EX | LOCK_NB))
        {
            throw new CoreLib_Api_Exception('Unable to obtain lock on file');
        }
    }
}

Your current test should pass as-written. Other than that, I can't think of anything.

Edit: They should pass as long as you are telling PHPUnit to expect the CoreLib_Api_Exception exception (just pointing it out because you didn't have it in your example).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top