1
0
mirror of https://github.com/danog/file.git synced 2024-11-30 04:19:39 +01:00
file/test/HandleTest.php

391 lines
12 KiB
PHP
Raw Permalink Normal View History

<?php
2015-08-13 01:02:41 +02:00
namespace Amp\File\Test;
use Amp\ByteStream\ClosedException;
2019-07-15 17:34:40 +02:00
use Amp\Delayed;
use Amp\File;
2017-05-18 05:58:39 +02:00
use Amp\PHPUnit\TestCase;
2019-08-22 23:33:03 +02:00
use Amp\Sync\Lock;
2019-08-22 21:32:07 +02:00
use Amp\TimeoutCancellationToken;
2019-07-15 17:34:40 +02:00
use function Amp\Promise\timeout;
2015-08-13 01:02:41 +02:00
abstract class HandleTest extends TestCase
{
protected function setUp()
{
2015-08-13 01:02:41 +02:00
Fixture::init();
File\StatCache::clear();
2015-08-13 01:02:41 +02:00
}
protected function tearDown()
{
2015-08-13 01:02:41 +02:00
Fixture::clear();
}
abstract protected function execute(callable $cb);
2016-07-21 01:33:03 +02:00
public function testWrite()
{
$this->execute(function () {
2019-08-22 21:32:07 +02:00
$path = Fixture::path() . "/write";
/** @var \Amp\File\Handle $handle */
$handle = yield File\open($path, "c+");
2015-08-13 01:02:41 +02:00
$this->assertSame(0, $handle->tell());
2017-06-20 07:58:30 +02:00
$handle->write("foo");
2015-08-13 01:02:41 +02:00
yield $handle->write("bar");
yield $handle->seek(0);
$contents = yield $handle->read();
2015-08-13 01:02:41 +02:00
$this->assertSame(6, $handle->tell());
$this->assertTrue($handle->eof());
$this->assertSame("foobar", $contents);
yield $handle->close();
});
}
public function testEmptyWrite()
{
$this->execute(function () {
2019-08-22 21:32:07 +02:00
$path = Fixture::path() . "/write";
$handle = yield File\open($path, "c+");
$this->assertSame(0, $handle->tell());
yield $handle->write("");
$this->assertSame(0, $handle->tell());
yield $handle->close();
});
}
public function testWriteAfterClose()
{
$this->execute(function () {
2019-08-22 21:32:07 +02:00
$path = Fixture::path() . "/write";
/** @var \Amp\File\Handle $handle */
$handle = yield File\open($path, "c+");
yield $handle->close();
$this->expectException(ClosedException::class);
yield $handle->write("bar");
});
}
public function testDoubleClose()
{
2017-06-21 14:07:49 +02:00
$this->execute(function () {
2019-08-22 21:32:07 +02:00
$path = Fixture::path() . "/write";
2017-06-21 14:07:49 +02:00
/** @var \Amp\File\Handle $handle */
$handle = yield File\open($path, "c+");
yield $handle->close();
$this->assertNull(yield $handle->close());
});
}
public function testWriteAfterEnd()
{
$this->execute(function () {
2019-08-22 21:32:07 +02:00
$path = Fixture::path() . "/write";
/** @var \Amp\File\Handle $handle */
$handle = yield File\open($path, "c+");
$this->assertSame(0, $handle->tell());
yield $handle->end("foo");
$this->expectException(ClosedException::class);
yield $handle->write("bar");
});
}
2019-03-01 17:38:04 +01:00
public function testWriteInAppendMode()
{
$this->execute(function () {
2019-08-22 21:32:07 +02:00
$path = Fixture::path() . "/write";
2019-03-01 17:38:04 +01:00
/** @var \Amp\File\Handle $handle */
$handle = yield File\open($path, "a+");
$this->assertSame(0, $handle->tell());
yield $handle->write("bar");
yield $handle->write("foo");
yield $handle->write("baz");
$this->assertSame(9, $handle->tell());
yield $handle->seek(0);
$this->assertSame(0, $handle->tell());
$this->assertSame("barfoobaz", yield $handle->read());
});
}
public function testReadingToEof()
{
$this->execute(function () {
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
2015-08-13 01:02:41 +02:00
$contents = "";
$position = 0;
$stat = yield File\stat(__FILE__);
2016-08-24 07:01:41 +02:00
$chunkSize = (int) \floor(($stat["size"] / 5));
2015-08-13 01:02:41 +02:00
while (!$handle->eof()) {
$chunk = yield $handle->read($chunkSize);
2015-08-13 01:02:41 +02:00
$contents .= $chunk;
$position += \strlen($chunk);
$this->assertSame($position, $handle->tell());
}
$this->assertNull(yield $handle->read());
$this->assertSame(yield File\get(__FILE__), $contents);
yield $handle->close();
2015-08-13 01:02:41 +02:00
});
}
public function testSequentialReads()
{
$this->execute(function () {
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
2015-08-13 01:02:41 +02:00
$contents = "";
$contents .= yield $handle->read(10);
$contents .= yield $handle->read(10);
2015-08-13 01:02:41 +02:00
$expected = \substr(yield File\get(__FILE__), 0, 20);
2015-08-13 01:02:41 +02:00
$this->assertSame($expected, $contents);
yield $handle->close();
2015-08-13 01:02:41 +02:00
});
}
public function testReadingFromOffset()
{
$this->execute(function () {
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
2015-08-13 01:02:41 +02:00
$this->assertSame(0, $handle->tell());
2016-08-30 21:05:14 +02:00
yield $handle->seek(10);
2015-08-13 01:02:41 +02:00
$this->assertSame(10, $handle->tell());
$chunk = yield $handle->read(90);
2015-08-13 01:02:41 +02:00
$this->assertSame(100, $handle->tell());
$expected = \substr(yield File\get(__FILE__), 10, 90);
2015-08-13 01:02:41 +02:00
$this->assertSame($expected, $chunk);
yield $handle->close();
2015-08-13 01:02:41 +02:00
});
}
/**
2016-08-30 21:05:14 +02:00
* @expectedException \Error
2015-08-13 01:02:41 +02:00
*/
public function testSeekThrowsOnInvalidWhence()
{
$this->execute(function () {
try {
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
yield $handle->seek(0, 99999);
} finally {
yield $handle->close();
}
2015-08-13 01:02:41 +02:00
});
}
public function testSeekSetCur()
{
$this->execute(function () {
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
2015-08-13 01:02:41 +02:00
$this->assertSame(0, $handle->tell());
2016-08-30 21:05:14 +02:00
yield $handle->seek(10);
2015-08-13 01:02:41 +02:00
$this->assertSame(10, $handle->tell());
2016-08-30 21:05:14 +02:00
yield $handle->seek(-10, \SEEK_CUR);
2015-08-13 01:02:41 +02:00
$this->assertSame(0, $handle->tell());
yield $handle->close();
2015-08-13 01:02:41 +02:00
});
}
public function testSeekSetEnd()
{
$this->execute(function () {
$size = yield File\size(__FILE__);
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
2015-08-13 01:02:41 +02:00
$this->assertSame(0, $handle->tell());
2016-08-30 21:05:14 +02:00
yield $handle->seek(-10, \SEEK_END);
2015-08-13 01:02:41 +02:00
$this->assertSame($size - 10, $handle->tell());
yield $handle->close();
2015-08-13 01:02:41 +02:00
});
}
public function testPath()
{
$this->execute(function () {
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
2015-08-13 01:02:41 +02:00
$this->assertSame(__FILE__, $handle->path());
yield $handle->close();
2015-08-13 01:02:41 +02:00
});
}
public function testMode()
{
$this->execute(function () {
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
2015-08-13 01:02:41 +02:00
$this->assertSame("r", $handle->mode());
yield $handle->close();
2015-08-13 01:02:41 +02:00
});
}
2019-07-15 17:34:40 +02:00
/**
2019-08-22 21:32:07 +02:00
* Try locking file exclusively.
*
* @param string $file File
* @param int $polling Polling interval
* @param int $timeout Lock timeout
* @return void
*/
private function tryLockExclusive(string $file, int $polling, int $timeout)
{
return File\lockExclusive($file, $polling, new TimeoutCancellationToken($timeout));
}
/**
* Try locking file in shared mode.
2019-07-15 17:34:40 +02:00
*
* @param string $file File
* @param int $polling Polling interval
* @param int $timeout Lock timeout
* @return void
*/
2019-08-22 21:32:07 +02:00
private function tryLockShared(string $file, int $polling, int $timeout)
2019-07-15 17:34:40 +02:00
{
2019-08-22 21:32:07 +02:00
return File\lockShared($file, $polling, new TimeoutCancellationToken($timeout));
2019-07-15 17:34:40 +02:00
}
public function testExclusiveLock()
{
$this->execute(function () {
$primary = null;
$secondary = null;
try {
try {
2019-08-22 21:32:07 +02:00
$primary = yield $this->tryLockExclusive(__FILE__, 100, 100);
2019-08-22 23:33:03 +02:00
$this->assertInstanceOf(Lock::class, $primary);
2019-07-15 17:34:40 +02:00
$unlocked = false;
2019-08-22 21:32:07 +02:00
$try = $this->tryLockShared(__FILE__, 100, 10000);
2019-07-15 17:34:40 +02:00
$try->onResolve(static function ($e, $secondaryUnlock) use (&$unlocked, &$secondary) {
if ($e) {
throw $e;
}
$unlocked = true;
$secondary = $secondaryUnlock;
});
$this->assertFalse($unlocked, "The lock wasn't acquired");
} finally {
if ($primary) {
2019-08-22 23:33:03 +02:00
$primary->release();
2019-07-15 17:34:40 +02:00
}
}
yield new Delayed(100 * 2);
$this->assertTrue($unlocked, "The lock wasn't released");
yield $try;
2019-08-22 23:33:03 +02:00
$this->assertInstanceOf(Lock::class, $secondary);
2019-07-15 17:34:40 +02:00
} finally {
if ($secondary) {
2019-08-22 23:33:03 +02:00
$secondary->release();
2019-07-15 17:34:40 +02:00
}
}
});
}
public function testSharedLock()
{
$this->execute(function () {
$primary = null;
$secondary = null;
try {
2019-08-22 21:32:07 +02:00
$primary = yield $this->tryLockShared(__FILE__, 100, 100);
2019-08-22 23:33:03 +02:00
$this->assertInstanceOf(Lock::class, $primary);
2019-08-22 21:32:07 +02:00
$secondary = yield $this->tryLockShared(__FILE__, 100, 100);
2019-08-22 23:33:03 +02:00
$this->assertInstanceOf(Lock::class, $secondary);
2019-07-15 17:34:40 +02:00
} finally {
if ($primary) {
2019-08-22 23:33:03 +02:00
$primary->release();
2019-07-15 17:34:40 +02:00
}
if ($secondary) {
2019-08-22 23:33:03 +02:00
$secondary->release();
2019-07-15 17:34:40 +02:00
}
}
});
}
public function testClose()
{
$this->execute(function () {
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
2015-08-13 01:02:41 +02:00
yield $handle->close();
$this->expectException(ClosedException::class);
yield $handle->read();
2015-08-13 01:02:41 +02:00
});
}
/**
* @depends testWrite
*/
public function testTruncateToSmallerSize()
{
$this->execute(function () {
2019-08-22 21:32:07 +02:00
$path = Fixture::path() . "/write";
/** @var \Amp\File\Handle $handle */
$handle = yield File\open($path, "c+");
$handle->write("foo");
yield $handle->write("bar");
yield $handle->truncate(4);
yield $handle->seek(0);
$contents = yield $handle->read();
$this->assertTrue($handle->eof());
$this->assertSame("foob", $contents);
yield $handle->write("bar");
$this->assertSame(7, $handle->tell());
yield $handle->seek(0);
$contents = yield $handle->read();
$this->assertSame("foobbar", $contents);
yield $handle->close();
});
}
/**
* @depends testWrite
*/
public function testTruncateToLargerSize()
{
$this->execute(function () {
2019-08-22 21:32:07 +02:00
$path = Fixture::path() . "/write";
/** @var \Amp\File\Handle $handle */
$handle = yield File\open($path, "c+");
yield $handle->write("foo");
yield $handle->truncate(6);
$this->assertSame(3, $handle->tell());
yield $handle->seek(0);
$contents = yield $handle->read();
$this->assertTrue($handle->eof());
$this->assertSame("foo\0\0\0", $contents);
yield $handle->write("bar");
$this->assertSame(9, $handle->tell());
yield $handle->seek(0);
$contents = yield $handle->read();
$this->assertSame("foo\0\0\0bar", $contents);
yield $handle->close();
});
}
2015-08-13 01:02:41 +02:00
}