In my application users can upload files and other users can download them. As a part of my controller integration test I move a couple of test files into the upload directory so I can test the download action.
The moving of files in setUp()
and removing them in teardown()
works, as long as I don't touch the files in a test. When the files were returned by a test, the tearDown()
is failing to remove them, I get the error:
Warning Error: unlink(C:\xampp\htdocs\2deal\keys_test\1_open.key): Resource temporarily unavailable
How can I fix this?
tearDown()
, but to no effect. Interestingly the fopen()
function succeeds, so the file isn't actually locked.unset($this->_response);
to the top of tearDown()
, but again to no avail.sleep(10);
add the start of tearDown()
, which also made no difference. So it's not a timing issue.The entire test file looks like this:
/**
* setUp method before each test
*/
public function setUp()
{
Configure::write('Keys.path', 'keys_test');
// Config writing must happen before parent::setUp()
parent::setUp();
$files = [
'1_open.key',
'1_close.key',
'2_open.key',
'2_close.key'
];
foreach ($files as $file)
{
copy(self::SOURCE_DIR . $file, ROOT . DS . Configure::read('Keys.path') . DS . $file);
}
}
/**
* tearDown method after each test
*/
public function tearDown()
{
parent::tearDown();
// Clear test upload directory
$files = glob(ROOT . DS . Configure::read('Keys.path') . DS . '*');
foreach ($files as $file)
{
if (is_file($file))
{
\unlink($file); // Delete file
}
}
}
/**
* Test getFile method
*
* @return void
*/
public function testGetFile()
{
$this->get('/carkeys/get-file/1/open.json');
$file = $this->_response->getFile();
$real_file = self::SOURCE_DIR . '1_open.key';
$this->assertResponseOk();
$this->assertFileEquals($file->path, $real_file);
}
The file isn't locked, but there's an open handle for it.
As of CakePHP 3.4, the response uses streams for the response body. It creates the streams when attaching body contents or files, and the stream is being opened immediately for writing or reading.
Long story short, close the stream before trying to delete the file:
$this->_response->getBody()->close();