From 18615ba81ae4314eb9881ef5bffefde7aa2376de Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 13:55:09 +0200 Subject: [PATCH 01/10] Add libuv --- src/Filesystem.php | 4 + src/ModeTypeDetector.php | 4 + src/Uv/Adapter.php | 593 ++++++++++++++++++++++++ src/Uv/OpenFlagResolver.php | 37 ++ src/Uv/PermissionFlagResolver.php | 73 +++ tests/AdapterTestAbstract.php | 335 +++++++++++++ tests/Eio/OpenFlagResolverTest.php | 5 + tests/Uv/AdapterTest.php | 22 + tests/Uv/OpenFlagResolverTest.php | 34 ++ tests/Uv/PermissionFlagResolverTest.php | 116 +++++ 10 files changed, 1223 insertions(+) create mode 100644 src/Uv/Adapter.php create mode 100644 src/Uv/OpenFlagResolver.php create mode 100644 src/Uv/PermissionFlagResolver.php create mode 100644 tests/AdapterTestAbstract.php create mode 100644 tests/Uv/AdapterTest.php create mode 100644 tests/Uv/OpenFlagResolverTest.php create mode 100644 tests/Uv/PermissionFlagResolverTest.php diff --git a/src/Filesystem.php b/src/Filesystem.php index db596906..e5c5d458 100644 --- a/src/Filesystem.php +++ b/src/Filesystem.php @@ -57,6 +57,10 @@ public static function getSupportedAdapters() { $adapters = []; + if (Uv\Adapter::isSupported()) { + $adapters[] = 'Uv'; + } + if (Eio\Adapter::isSupported()) { $adapters[] = 'Eio'; } diff --git a/src/ModeTypeDetector.php b/src/ModeTypeDetector.php index de2f5594..02ca6246 100644 --- a/src/ModeTypeDetector.php +++ b/src/ModeTypeDetector.php @@ -36,6 +36,10 @@ public function __construct(FilesystemInterface $filesystem) */ public function detect(array $node) { + if (isset($node['mode'])) { + return $this->walkMapping($node); + } + return $this->filesystem->getAdapter()->stat($node['path'])->then(function ($stat) { return $this->walkMapping($stat); }); diff --git a/src/Uv/Adapter.php b/src/Uv/Adapter.php new file mode 100644 index 00000000..787d3c85 --- /dev/null +++ b/src/Uv/Adapter.php @@ -0,0 +1,593 @@ + 0, + 'symlinkFlags' => 0, + ]; + + /** + * @var int + */ + protected $workCounter = 0; + + /** + * @var TimerInterface|null + */ + protected $workTimer; + + /** + * @var int + */ + protected $workInterval; + + /** + * @inheritDoc + */ + public function __construct(ExtUvLoop $loop, array $options = []) + { + $this->loop = $loop; + $this->options = \array_merge($this->options, $options); + $this->workInterval = (int) (\PHP_INT_MAX / 1000) - 1; + + $this->openFlagResolver = new OpenFlagResolver(); + $this->permissionFlagResolver = new PermissionFlagResolver(); + } + + /** + * @return bool + */ + public static function isSupported() + { + return \extension_loaded('uv'); + } + + /** To be removed */ + public function getInvoker() {} + public function setInvoker(CallInvokerInterface $invoker) {} + + /** + * @return LoopInterface + */ + public function getLoop() + { + return $this->loop; + } + + /** + * {@inheritDoc} + */ + public function getFilesystem() + { + return $this->filesystem; + } + + /** + * {@inheritDoc} + */ + public function setFilesystem(FilesystemInterface $filesystem) + { + $this->filesystem = $filesystem; + + $this->typeDetectors = [ + MappedTypeDetector::createDefault($this->filesystem), + new ModeTypeDetector($this->filesystem), + ]; + } + + /** + * Call the underlying filesystem. + * + * @internal + * @param callable $function + * @param array $args + * @param callable $callable + * @return PromiseInterface + */ + public function callFilesystem($function, $args, $callable = null) + { + $deferred = new Deferred(); + + if (!\is_callable($callable)) { + $deferred->reject(new InvalidArgumentException('Invalid callable')); + return $deferred->promise(); + } + + $this->register(); + $args[] = function (...$args) use ($deferred, $callable) { + $this->unregister(); + + try { + $deferred->resolve(\call_user_func_array($callable, $args)); + } catch (\Throwable $e) { + $deferred->reject($e); + } + }; + + try { + \call_user_func($function, $this->loop->getUvLoop(), ...$args); + } catch (\Throwable $e) { + $deferred->reject($e); + } + + return $deferred->promise(); + } + + /** + * @param string $path + * @param int $mode + * @return PromiseInterface + */ + public function chmod($path, $mode) + { + return $this->callFilesystem('uv_fs_chmod', [ + $path, + $mode, + ], function ($result) { + if ($result === false) { + throw new Exception('Unable to set chmod on target'); + } + }); + } + + /** + * @param string $path + * @param mixed $mode + * @return PromiseInterface + */ + public function mkdir($path, $mode = self::CREATION_MODE) + { + return $this->callFilesystem('uv_fs_mkdir', [ + $path, + $this->permissionFlagResolver->resolve($mode), + ], function ($result) { + if ($result === false) { + throw new Exception('Unable to create directory at path'); + } + }); + } + + /** + * @param string $path + * @return PromiseInterface + */ + public function rmdir($path) + { + return $this->callFilesystem('uv_fs_rmdir', [ + $path, + ], function ($result) { + if ($result === false) { + throw new Exception('Unable to delete directory'); + } + }); + } + + /** + * @param string $path + * @return PromiseInterface + */ + public function unlink($path) + { + return $this->callFilesystem('uv_fs_unlink', [ + $path, + ], function ($result) { + if ($result === false) { + throw new Exception('Unable to delete the target'); + } + }); + } + + /** + * @param string $path + * @param int $uid + * @param int $gid + * @return PromiseInterface + */ + public function chown($path, $uid, $gid) + { + return $this->callFilesystem('uv_fs_chown', [ + $path, + $uid, + $gid, + ], function ($result) { + if ($result === false) { + throw new Exception('Unable to chown the target'); + } + }); + } + + /** + * @param string $filename + * @return PromiseInterface + */ + public function stat($filename) + { + return $this->callFilesystem('uv_fs_lstat', [ + $filename, + ], function ($bool, $stat = null) { + if ($bool !== true) { + throw new Exception('Unable to stat the target'); + } + + $stat['blksize'] = $stat['blksize'] ?? -1; + $stat['blocks'] = $stat['blocks'] ?? -1; + $stat['atime'] = new DateTime('@' . $stat['atime']); + $stat['mtime'] = new DateTime('@' . $stat['mtime']); + $stat['ctime'] = new DateTime('@' . $stat['ctime']); + + return $stat; + }); + } + + /** + * @param string $path + * @return PromiseInterface + */ + public function ls($path) + { + return ObjectStreamSink::promise($this->lsStream($path)); + } + + /** + * @param string $path + * @return ObjectStream + */ + public function lsStream($path) + { + $stream = new ObjectStream(); + + $this->callFilesystem('uv_fs_scandir', [ + $path, + $this->options['lsFlags'], + ], function ($bool, $result = null) use ($path, $stream) { + if ($bool !== true) { + throw new Exception('Unable to list the directory'); + } + + $this->processLsContents($path, $result, $stream); + }); + + return $stream; + } + + protected function processLsContents($basePath, $result, ObjectStream $stream) + { + $this->register(); + $promises = []; + + foreach ($result as $entry) { + $path = $basePath . \DIRECTORY_SEPARATOR . $entry; + + $promises[] = $this->stat($path)->then(function ($stat) use ($path, $stream) { + $node = [ + 'path' => $path, + 'mode' => $stat['mode'], + 'type' => null, + ]; + + return \React\Filesystem\detectType($this->typeDetectors, $node)->then(function (NodeInterface $node) use ($stream) { + $stream->write($node); + }); + }); + } + + \React\Promise\all($promises)->then(function () use ($stream) { + $this->unregister(); + $stream->close(); + }); + } + + /** + * @param string $path + * @param mixed $mode + * @return PromiseInterface + */ + public function touch($path, $mode = self::CREATION_MODE) + { + return $this->appendContents($path, '')->then(function () use ($path) { + return $this->callFilesystem('uv_fs_utime', [ + $path, + \time(), + \time(), + ], function ($result) { + if ($result === false) { + throw new Exception('Unable to touch target'); + } + }); + }); + } + + /** + * @param string $path + * @param string $flags + * @param mixed $mode + * @return PromiseInterface + */ + public function open($path, $flags, $mode = self::CREATION_MODE) + { + return $this->callFilesystem('uv_fs_open', [ + $path, + $this->openFlagResolver->resolve($flags), + $this->permissionFlagResolver->resolve($mode), + ], function ($fd) { + if ($fd === false) { + throw new Exception('Unable to open file, make sure the file exists and is readable'); + } + + $this->fileDescriptors[(int) $fd] = $fd; + return (int) $fd; + }); + } + + /** + * @param string $fileDescriptor + * @param int $length + * @param int $offset + * @return PromiseInterface + */ + public function read($fileDescriptor, $length, $offset) + { + if (empty($this->fileDescriptors[$fileDescriptor])) { + return \React\Promise\reject(new Exception('Unknown file descriptor')); + } + + $fd = $this->fileDescriptors[$fileDescriptor]; + + return $this->callFilesystem('uv_fs_read', [ + $fd, + $offset, + $length, + ], function ($fd, $nread, $buffer) { + return $buffer; + }); + } + + /** + * @param string $fileDescriptor + * @param string $data + * @param int $length Unused. + * @param int $offset + * @return PromiseInterface + */ + public function write($fileDescriptor, $data, $length, $offset) + { + if (empty($this->fileDescriptors[$fileDescriptor])) { + return \React\Promise\reject(new Exception('Unknown file descriptor')); + } + + $fd = $this->fileDescriptors[$fileDescriptor]; + + return $this->callFilesystem('uv_fs_write', [ + $fd, + $data, + $offset, + ], function ($fd, $result) { + return $result; + }); + } + + /** + * @param string $fileDescriptor + * @return PromiseInterface + */ + public function close($fileDescriptor) + { + if (empty($this->fileDescriptors[$fileDescriptor])) { + return \React\Promise\reject(new Exception('Unknown file descriptor')); + } + + $fd = $this->fileDescriptors[$fileDescriptor]; + unset($this->fileDescriptors[$fileDescriptor]); + + return $this->callFilesystem('uv_fs_close', [ + $fd, + ], function () { + // NO-OP + }); + } + + /** + * Reads the entire file. + * + * This is an optimization for adapters which can optimize + * the open -> (seek ->) read -> close sequence into one call. + * + * @param string $path + * @param int $offset + * @param int|null $length + * @return PromiseInterface + */ + public function getContents($path, $offset = 0, $length = null) + { + if ($length === null) { + return $this->stat($path)->then(function ($stat) use ($path, $offset) { + return $this->getContents($path, $offset, $stat['size']); + }); + } + + return $this->open($path, 'r')->then(function ($fd) use ($offset, $length) { + return $this->read($fd, $length, $offset)->always(function () use ($fd) { + return $this->close($fd); + }); + }); + } + + /** + * Writes the given content to the specified file. + * If the file exists, the file is truncated. + * If the file does not exist, the file will be created. + * + * This is an optimization for adapters which can optimize + * the open -> write -> close sequence into one call. + * + * @param string $path + * @param string $content + * @return PromiseInterface + * @see AdapterInterface::appendContents() + */ + public function putContents($path, $content) + { + return $this->open($path, 'ctw')->then(function ($fd) use ($content) { + return $this->write($fd, $content, strlen($content), 0)->always(function () use ($fd) { + return $this->close($fd); + }); + }); + } + + /** + * Appends the given content to the specified file. + * If the file does not exist, the file will be created. + * + * This is an optimization for adapters which can optimize + * the open -> write -> close sequence into one call. + * + * @param string $path + * @param string $content + * @return PromiseInterface + * @see AdapterInterface::putContents() + */ + public function appendContents($path, $content) + { + return $this->open($path, 'ca')->then(function ($fd) use ($content) { + return $this->write($fd, $content, strlen($content), 0)->always(function () use ($fd) { + return $this->close($fd); + }); + }); + } + + /** + * @param string $fromPath + * @param string $toPath + * @return PromiseInterface + */ + public function rename($fromPath, $toPath) + { + return $this->callFilesystem('uv_fs_rename', [ + $fromPath, + $toPath, + ], function ($result) { + if ($result === false) { + throw new Exception('Unable to rename target'); + } + }); + } + + /** + * @param string $path + * @return PromiseInterface + */ + public function readlink($path) + { + return $this->callFilesystem('uv_fs_readlink', [ + $path, + ], function ($bool, $result) { + if ($bool === false) { + throw new Exception('Unable to read link of target'); + } + + return $result; + }); + } + + /** + * @param string $fromPath + * @param string $toPath + * @return PromiseInterface + */ + public function symlink($fromPath, $toPath) + { + return $this->callFilesystem('uv_fs_symlink', [ + $fromPath, + $toPath, + $this->options['symlinkFlags'], + ], function ($result) { + if ($result === false) { + throw new Exception('Unable to create a symlink for the target'); + } + }); + } + + /** + * @inheritDoc + */ + public function detectType($path) + { + return \React\Filesystem\detectType($this->typeDetectors, [ + 'path' => $path, + ]); + } + + /** + * Registers work and possibly the timer. + */ + protected function register() + { + if ($this->workCounter++ <= 0) { + $this->workTimer = $this->loop->addTimer($this->workInterval, function () {}); + } + } + + /** + * Unregisters work and possibly the timer. + */ + protected function unregister() + { + if (--$this->workCounter <= 0) { + $this->loop->cancelTimer($this->workTimer); + } + } +} diff --git a/src/Uv/OpenFlagResolver.php b/src/Uv/OpenFlagResolver.php new file mode 100644 index 00000000..1c441ff2 --- /dev/null +++ b/src/Uv/OpenFlagResolver.php @@ -0,0 +1,37 @@ + \UV::O_RDWR, + 'a' => \UV::O_APPEND, + 'c' => \UV::O_CREAT, + 'e' => \UV::O_EXCL, + 'r' => \UV::O_RDONLY, + 't' => \UV::O_TRUNC, + 'w' => \UV::O_WRONLY, + ]; + + /** + * {@inheritDoc} + */ + public function defaultFlags() + { + return static::DEFAULT_FLAG; + } + + /** + * {@inheritDoc} + */ + public function flagMapping() + { + return $this->flagMapping; + } +} diff --git a/src/Uv/PermissionFlagResolver.php b/src/Uv/PermissionFlagResolver.php new file mode 100644 index 00000000..0849c20a --- /dev/null +++ b/src/Uv/PermissionFlagResolver.php @@ -0,0 +1,73 @@ + [ + 'w' => \UV::S_IWOTH, + 'x' => \UV::S_IXOTH, + 'r' => \UV::S_IROTH, + ], + 'group' => [ + 'w' => \UV::S_IWGRP, + 'x' => \UV::S_IXGRP, + 'r' => \UV::S_IRGRP, + ], + 'universe' => [ + 'w' => \UV::S_IWUSR, + 'x' => \UV::S_IXUSR, + 'r' => \UV::S_IRUSR, + ], + ]; + + /** + * {@inheritDoc} + */ + public function defaultFlags() + { + return static::DEFAULT_FLAG; + } + + /** + * {@inheritDoc} + */ + public function flagMapping() + { + return $this->flagMapping[$this->currentScope]; + } + + /** + * {@inheritDoc} + */ + public function resolve($flag, $flags = null, $mapping = null) + { + if (\is_int($flag)) { + return $flag; + } + + $resultFlags = 0; + $start = 0; + + foreach ([ + 'universe', + 'group', + 'user', + ] as $scope) { + $this->currentScope = $scope; + $start -= 3; + $chunk = \substr($flag, $start, 3); + $resultFlags |= parent::resolve($chunk, $flags, $mapping); + } + + return $resultFlags; + } +} \ No newline at end of file diff --git a/tests/AdapterTestAbstract.php b/tests/AdapterTestAbstract.php new file mode 100644 index 00000000..36a4f491 --- /dev/null +++ b/tests/AdapterTestAbstract.php @@ -0,0 +1,335 @@ +loop = Factory::create(); + $this->adapter = $this->createAdapter(); + $this->filesystem = Filesystem::createFromAdapter($this->adapter); + + \clearstatcache($this->tmpDir.'testdir'); + \clearstatcache($this->tmpDir.'testdir2'); + + \clearstatcache($this->tmpDir.'testfile'); + \clearstatcache($this->tmpDir.'testfile2'); + } + + public function isSupported() + { + $this->assertTrue($this->adapter->isSupported()); + } + + public function testGetLoop() + { + $this->assertSame($this->loop, $this->adapter->getLoop()); + } + + public function testGetFilesystem() + { + $this->assertSame($this->filesystem, $this->adapter->getFilesystem()); + } + + public function testSetFilesystem() + { + $fs = Filesystem::create($this->loop); + $this->adapter->setFilesystem($fs); + + $this->assertSame($fs, $this->adapter->getFilesystem()); + $this->adapter->setFilesystem($this->filesystem); + } + + public function testMkdirAndRmdir() + { + $path = $this->tmpDir.'testdir'; + @\rmdir($path); + + $this->await($this->adapter->mkdir($path), $this->adapter->getLoop()); + $this->assertNotFalse(\realpath($path)); + + $this->await($this->adapter->rmdir($path), $this->adapter->getLoop()); + $this->assertFalse(\realpath($path)); + } + + public function testUnlink() + { + $path = $this->tmpDir . \uniqid('', true); + $this->assertTrue(\touch($path, \time(), \time())); + + $this->await($this->adapter->unlink($path), $this->adapter->getLoop()); + $this->assertFalse(\file_exists($path)); + } + + /** + * @group permissions + */ + public function testChmod() + { + $path = $this->tmpDir . \uniqid('', true); + $this->assertTrue(\touch($path, \time(), \time())); + + $this->await($this->adapter->chmod($path, 0660), $this->adapter->getLoop()); + $this->assertSame(0660, (\fileperms($path) & 0660)); + \unlink($path); + } + + /** + * @group permissions + */ + public function testChown() + { + if(DIRECTORY_SEPARATOR === '\\') { + return $this->markTestSkipped('Unsupported on Windows'); + } + + $path = $this->tmpDir . \uniqid('', true); + $this->assertTrue(\touch($path, \time(), \time())); + + $this->await($this->adapter->chown($path, 0, 2), $this->adapter->getLoop()); + $stat = \stat($path); + + $this->assertSame(0, $stat['uid']); + $this->assertSame(2, $stat['gid']); + @\unlink($path); + } + + public function testStat() + { + if(DIRECTORY_SEPARATOR === '\\') { + return $this->markTestSkipped('Unsupported on Windows (different dev, rdev and ino)'); + } + + $path = $this->tmpDir . \uniqid('', true); + $this->assertTrue(\touch($path, \time(), \time())); + + $stat = $this->await($this->adapter->stat($path), $this->adapter->getLoop()); + + $realStat = \stat($path); + $realStat = [ + 'dev' => $realStat['dev'], + 'ino' => $realStat['ino'], + 'mode' => $realStat['mode'], + 'nlink' => $realStat['nlink'], + 'uid' => $realStat['uid'], + 'size' => $realStat['size'], + 'gid' => $realStat['gid'], + 'rdev' => $realStat['rdev'], + 'blksize' => $realStat['blksize'], + 'blocks' => $realStat['blocks'], + 'atime' => (new DateTime('@' . $realStat['atime'])), + 'mtime' => (new DateTime('@' . $realStat['mtime'])), + 'ctime' => (new DateTime('@' . $realStat['ctime'])), + ]; + + $this->assertEquals($realStat, $stat); + @\unlink($path); + } + + public function testLs() + { + $path = $this->tmpDir . \uniqid('', true); + $this->assertTrue(\touch($path, \time(), \time())); + + $ls = $this->await($this->adapter->ls($this->tmpDir), $this->adapter->getLoop()); + @\unlink($path); + + $this->assertSame(1, \count($ls)); + $this->assertInstanceOf(FileInterface::class, $ls[0]); + } + + public function testLsStream() + { + $path = $this->tmpDir . \uniqid('', true); + $this->assertTrue(\touch($path, time(), time())); + + $stream = $this->adapter->lsStream($this->tmpDir); + $this->assertInstanceOf(ObjectStream::class, $stream); + + $ls = $this->await(ObjectStreamSink::promise($stream), $this->adapter->getLoop()); + @\unlink($path); + + $this->assertSame(1, \count($ls)); + $this->assertInstanceOf(FileInterface::class, $ls[0]); + } + + public function testTouch() + { + $path = $this->tmpDir . \uniqid('', true); + $this->await($this->adapter->touch($path), $this->adapter->getLoop()); + + $this->assertTrue(file_exists($path)); + @\unlink($path); + } + + public function testTouchExisting() + { + $path = $this->tmpDir . \uniqid('', true); + \touch($path, \time() - 50, \time() - 50); + $this->await($this->adapter->touch($path), $this->adapter->getLoop()); + + $this->assertTrue(file_exists($path)); + @\unlink($path); + } + + public function testOpenReadWriteClose() + { + $path = $this->tmpDir . \uniqid('', true); + + $file = $this->await($this->adapter->open($path, 'cw'), $this->adapter->getLoop()); + + $length = $this->await($this->adapter->write($file, 'hello world', 11, 0), $this->adapter->getLoop()); + $this->assertSame(11, $length); + + $this->await($this->adapter->close($file), $this->adapter->getLoop()); + + $file2 = $this->await($this->adapter->open($path, 'r'), $this->adapter->getLoop()); + + $contents = $this->await($this->adapter->read($file2, 11, 0), $this->adapter->getLoop()); + $this->assertSame('hello world', $contents); + + $this->await($this->adapter->close($file2), $this->adapter->getLoop()); + @\unlink($path); + } + + public function testRename() + { + $path = $this->tmpDir . \uniqid('', true); + $path2 = $this->tmpDir . \uniqid('', true); + $this->assertTrue(\touch($path, \time(), \time())); + + $this->await($this->adapter->rename($path, $path2), $this->adapter->getLoop()); + $this->assertFalse(\file_exists($path)); + $this->assertTrue(\file_exists($path2)); + + @\unlink($path); + @\unlink($path2); + } + + public function testConstructLink() + { + $path = $this->tmpDir.'testdir-cl'; + $path2 = $this->tmpDir.'testdir-cl2'; + + $this->assertTrue(\mkdir($path)); + $this->assertTrue(\symlink($path, $path2)); + + $link = $this->await($this->filesystem->constructLink($path2), $this->adapter->getLoop()); + $this->assertInstanceOf(LinkInterface::class, $link); + + @\rmdir($path); + @\unlink($path2); + } + + public function testSymlinkAndReadlink() + { + $path = $this->tmpDir.'testdir-rl'; + $path2 = $this->tmpDir.'testdir-rl2'; + + $this->assertTrue(\mkdir($path)); + $this->assertFalse(\realpath($path2)); + + $this->await($this->adapter->symlink($path, $path2), $this->adapter->getLoop()); + $this->assertSame(\realpath($path), \realpath(\readlink($path2))); // realpath is for Windows + + $link = $this->await($this->adapter->readlink($path2), $this->adapter->getLoop()); + $this->assertSame($path, $link); + + @\rmdir($path); + @\unlink($path2); + } + + public function testDetectType() + { + $path = $this->tmpDir . \uniqid('', true); + $this->assertTrue(\touch($path, \time(), \time())); + + $type = $this->await($this->adapter->detectType($path), $this->adapter->getLoop()); + $this->assertInstanceOf(FileInterface::class, $type); + \unlink($path); + } + + public function testDetectTypeLink() + { + $path = $this->tmpDir . \uniqid('', true); + $this->await($this->adapter->symlink(__FILE__, $path), $this->adapter->getLoop()); + + $type = $this->await($this->adapter->detectType($path), $this->adapter->getLoop()); + $this->assertInstanceOf(LinkInterface::class, $type); + \unlink($path); + } + + public function testGetContents() + { + $contents = $this->await($this->adapter->getContents(__FILE__), $this->loop); + $this->assertSame(\file_get_contents(__FILE__), $contents); + } + + public function testGetContentsMinMax() + { + $contents = $this->await($this->adapter->getContents(__FILE__, 5, 10), $this->loop); + $this->assertSame(\file_get_contents(__FILE__, false, null, 5, 10), $contents); + } + + public function testPutContents() + { + $tempFile = $this->tmpDir . \uniqid('', true); + $contents = \sha1_file(__FILE__); + + $this->await($this->adapter->putContents($tempFile, $contents), $this->loop); + $this->assertSame($contents, \file_get_contents($tempFile)); + } + + public function testPutContentsOverwrite() + { + $tempFile = $this->tmpDir . \uniqid('', true); + $contents = \sha1_file(__FILE__); + + \file_put_contents($tempFile, \md5($contents)); + + $this->await($this->adapter->putContents($tempFile, $contents), $this->loop); + $this->assertSame($contents, \file_get_contents($tempFile)); + } + + public function testAppendContents() + { + $tempFile = $this->tmpDir . \uniqid('', true); + $contents = \sha1_file(__FILE__); + + \file_put_contents($tempFile, $contents); + $time = \sha1(\time()); + $contents .= $time; + + $this->await($this->adapter->appendContents($tempFile, $time), $this->loop); + $this->assertSame($contents, \file_get_contents($tempFile)); + } +} diff --git a/tests/Eio/OpenFlagResolverTest.php b/tests/Eio/OpenFlagResolverTest.php index 94ce5587..71194c9f 100644 --- a/tests/Eio/OpenFlagResolverTest.php +++ b/tests/Eio/OpenFlagResolverTest.php @@ -10,6 +10,11 @@ */ class OpenFlagResolverTest extends AbstractFlagResolverTest { + /** + * @var OpenFlagResolver + */ + protected $resolver; + public function setUp() { parent::setUp(); diff --git a/tests/Uv/AdapterTest.php b/tests/Uv/AdapterTest.php new file mode 100644 index 00000000..d825a1b0 --- /dev/null +++ b/tests/Uv/AdapterTest.php @@ -0,0 +1,22 @@ +loop)); + } +} diff --git a/tests/Uv/OpenFlagResolverTest.php b/tests/Uv/OpenFlagResolverTest.php new file mode 100644 index 00000000..0b8d249a --- /dev/null +++ b/tests/Uv/OpenFlagResolverTest.php @@ -0,0 +1,34 @@ +resolver = new OpenFlagResolver(); + } + + public function tearDown() + { + unset($this->resolver); + parent::tearDown(); + } + + public function testDefaultFlags() + { + $this->assertSame(OpenFlagResolver::DEFAULT_FLAG, $this->resolver->defaultFlags()); + } +} diff --git a/tests/Uv/PermissionFlagResolverTest.php b/tests/Uv/PermissionFlagResolverTest.php new file mode 100644 index 00000000..8a685f1a --- /dev/null +++ b/tests/Uv/PermissionFlagResolverTest.php @@ -0,0 +1,116 @@ +assertSame($result, $resolver->resolve($flags)); + } +} From 93c9fe5e360b357534be8e7b23beb9106f493a32 Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 13:58:55 +0200 Subject: [PATCH 02/10] Add libuv to travis --- composer.lock | 354 ++++++++++++++++++++++++++++++++----------------- travis-init.sh | 10 ++ 2 files changed, 243 insertions(+), 121 deletions(-) diff --git a/composer.lock b/composer.lock index 04110066..addd21d1 100644 --- a/composer.lock +++ b/composer.lock @@ -1,7 +1,7 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "0301b26ae1ada823f6fbeb0b71fc57bb", @@ -268,16 +268,16 @@ }, { "name": "paragonie/random_compat", - "version": "v2.0.11", + "version": "v2.0.18", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", + "reference": "0a58ef6e3146256cc3dc7cc393927bcc7d1b72db", "shasum": "" }, "require": { @@ -309,23 +309,24 @@ "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ "csprng", + "polyfill", "pseudorandom", "random" ], - "time": "2017-09-27T21:40:39+00:00" + "time": "2019-01-03T20:59:08+00:00" }, { "name": "react/cache", - "version": "v0.4.2", + "version": "v0.5.0", "source": { "type": "git", "url": "https://github.com/reactphp/cache.git", - "reference": "75494f26b4ef089db9bf8c90b63c296246e099e8" + "reference": "7d7da7fb7574d471904ba357b39bbf110ccdbf66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/75494f26b4ef089db9bf8c90b63c296246e099e8", - "reference": "75494f26b4ef089db9bf8c90b63c296246e099e8", + "url": "https://api.github.com/repos/reactphp/cache/zipball/7d7da7fb7574d471904ba357b39bbf110ccdbf66", + "reference": "7d7da7fb7574d471904ba357b39bbf110ccdbf66", "shasum": "" }, "require": { @@ -352,20 +353,20 @@ "promise", "reactphp" ], - "time": "2017-12-20T16:47:13+00:00" + "time": "2018-06-25T12:52:40+00:00" }, { "name": "react/child-process", - "version": "v0.5.2", + "version": "v0.6.1", "source": { "type": "git", "url": "https://github.com/reactphp/child-process.git", - "reference": "aae49d7f1340bafb695b9af3ce4421ea41a39620" + "reference": "6895afa583d51dc10a4b9e93cd3bce17b3b77ac3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/aae49d7f1340bafb695b9af3ce4421ea41a39620", - "reference": "aae49d7f1340bafb695b9af3ce4421ea41a39620", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/6895afa583d51dc10a4b9e93cd3bce17b3b77ac3", + "reference": "6895afa583d51dc10a4b9e93cd3bce17b3b77ac3", "shasum": "" }, "require": { @@ -375,7 +376,8 @@ "react/stream": "^1.0 || ^0.7.6" }, "require-dev": { - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35", + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35", + "react/socket": "^1.0", "sebastian/environment": "^3.0 || ^2.0 || ^1.0" }, "type": "library", @@ -394,25 +396,25 @@ "process", "reactphp" ], - "time": "2018-01-18T14:53:06+00:00" + "time": "2019-02-15T13:48:16+00:00" }, { "name": "react/dns", - "version": "v0.4.13", + "version": "v0.4.17", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "7d1e08c300fd7de600810883386ee5e2a64898f4" + "reference": "0f30c6ceb71504d359d51132a97e1703051f1589" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/7d1e08c300fd7de600810883386ee5e2a64898f4", - "reference": "7d1e08c300fd7de600810883386ee5e2a64898f4", + "url": "https://api.github.com/repos/reactphp/dns/zipball/0f30c6ceb71504d359d51132a97e1703051f1589", + "reference": "0f30c6ceb71504d359d51132a97e1703051f1589", "shasum": "" }, "require": { "php": ">=5.3.0", - "react/cache": "~0.4.0|~0.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5 || ^0.4 || ^0.3", "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", "react/promise": "^2.1 || ^1.2.1", "react/promise-timer": "^1.2", @@ -420,7 +422,7 @@ }, "require-dev": { "clue/block-react": "^1.2", - "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { @@ -439,31 +441,32 @@ "dns-resolver", "reactphp" ], - "time": "2018-02-27T12:51:22+00:00" + "time": "2019-04-01T07:31:55+00:00" }, { "name": "react/event-loop", - "version": "v0.5.2", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "e94985d93c689c554265b01014f8c3064921ca27" + "reference": "a0ecac955c67b57c40fe4a1b88a7cca1b58c982d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/e94985d93c689c554265b01014f8c3064921ca27", - "reference": "e94985d93c689c554265b01014f8c3064921ca27", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/a0ecac955c67b57c40fe4a1b88a7cca1b58c982d", + "reference": "a0ecac955c67b57c40fe4a1b88a7cca1b58c982d", "shasum": "" }, "require": { "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "~4.8.35 || ^5.7 || ^6.4" + "phpunit/phpunit": "^7.0 || ^6.4 || ^5.7 || ^4.8.35" }, "suggest": { "ext-event": "~1.0 for ExtEventLoop", - "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" }, "type": "library", "autoload": { @@ -480,20 +483,20 @@ "asynchronous", "event-loop" ], - "time": "2018-04-24T11:23:06+00:00" + "time": "2019-02-07T16:19:49+00:00" }, { "name": "react/promise", - "version": "v2.5.1", + "version": "v2.7.1", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "62785ae604c8d69725d693eb370e1d67e94c4053" + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/62785ae604c8d69725d693eb370e1d67e94c4053", - "reference": "62785ae604c8d69725d693eb370e1d67e94c4053", + "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d", + "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d", "shasum": "" }, "require": { @@ -526,7 +529,7 @@ "promise", "promises" ], - "time": "2017-03-25T12:08:31+00:00" + "time": "2019-01-07T21:25:54+00:00" }, { "name": "react/promise-stream", @@ -586,22 +589,22 @@ }, { "name": "react/promise-timer", - "version": "v1.2.1", + "version": "v1.5.1", "source": { "type": "git", "url": "https://github.com/reactphp/promise-timer.git", - "reference": "9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd" + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd", - "reference": "9b4cd9cbe7457e0d87fe8aa7ccceab8a2c830fbd", + "url": "https://api.github.com/repos/reactphp/promise-timer/zipball/35fb910604fd86b00023fc5cda477c8074ad0abc", + "reference": "35fb910604fd86b00023fc5cda477c8074ad0abc", "shasum": "" }, "require": { "php": ">=5.3", "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "~2.1|~1.2" + "react/promise": "^2.7.0 || ^1.2.1" }, "require-dev": { "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" @@ -612,7 +615,7 @@ "React\\Promise\\Timer\\": "src/" }, "files": [ - "src/functions.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -626,7 +629,7 @@ } ], "description": "A trivial implementation of timeouts for Promises, built on top of ReactPHP.", - "homepage": "https://github.com/react/promise-timer", + "homepage": "https://github.com/reactphp/promise-timer", "keywords": [ "async", "event-loop", @@ -635,20 +638,20 @@ "timeout", "timer" ], - "time": "2017-12-22T15:41:41+00:00" + "time": "2019-03-27T18:10:32+00:00" }, { "name": "react/socket", - "version": "v0.8.10", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", - "reference": "d3957313c92b539537fccc80170c05a27ec25796" + "reference": "23b7372bb25cea934f6124f5bdac34e30161959e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/d3957313c92b539537fccc80170c05a27ec25796", - "reference": "d3957313c92b539537fccc80170c05a27ec25796", + "url": "https://api.github.com/repos/reactphp/socket/zipball/23b7372bb25cea934f6124f5bdac34e30161959e", + "reference": "23b7372bb25cea934f6124f5bdac34e30161959e", "shasum": "" }, "require": { @@ -656,9 +659,9 @@ "php": ">=5.3.0", "react/dns": "^0.4.13", "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "^2.1 || ^1.2", - "react/promise-timer": "~1.0", - "react/stream": "^1.0 || ^0.7.1" + "react/promise": "^2.6.0 || ^1.2.1", + "react/promise-timer": "^1.4.0", + "react/stream": "^1.1" }, "require-dev": { "clue/block-react": "^1.2", @@ -682,20 +685,20 @@ "reactphp", "stream" ], - "time": "2018-02-28T09:32:38+00:00" + "time": "2019-01-07T14:10:13+00:00" }, { "name": "react/stream", - "version": "v0.7.7", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/reactphp/stream.git", - "reference": "10100896018fd847a257cd81143b8e1b7be08e40" + "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/10100896018fd847a257cd81143b8e1b7be08e40", - "reference": "10100896018fd847a257cd81143b8e1b7be08e40", + "url": "https://api.github.com/repos/reactphp/stream/zipball/50426855f7a77ddf43b9266c22320df5bf6c6ce6", + "reference": "50426855f7a77ddf43b9266c22320df5bf6c6ce6", "shasum": "" }, "require": { @@ -728,7 +731,7 @@ "stream", "writable" ], - "time": "2018-01-19T15:04:38+00:00" + "time": "2019-01-01T16:15:09+00:00" }, { "name": "tivie/php-os-detector", @@ -780,23 +783,25 @@ }, { "name": "wyrihaximus/cpu-core-detector", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/WyriHaximus/php-cpu-core-detector.git", - "reference": "459dbf380172f06de6bf1aaecb9b1a003f7937b1" + "reference": "fff4194540a8111c298e5e707bcfc778c4c9ddbb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/php-cpu-core-detector/zipball/459dbf380172f06de6bf1aaecb9b1a003f7937b1", - "reference": "459dbf380172f06de6bf1aaecb9b1a003f7937b1", + "url": "https://api.github.com/repos/WyriHaximus/php-cpu-core-detector/zipball/fff4194540a8111c298e5e707bcfc778c4c9ddbb", + "reference": "fff4194540a8111c298e5e707bcfc778c4c9ddbb", "shasum": "" }, "require": { "php": "^5.4||^7.0", - "react/child-process": "^0.5 || ^0.4", - "tivie/php-os-detector": "^1.0", - "wyrihaximus/react-child-process-promise": "^2.0", + "react/child-process": "^0.6 || ^0.5 || ^0.4", + "react/socket": "^1.2", + "tivie/php-os-detector": "^1.1", + "wyrihaximus/file-descriptors": "^1.0 || ^0.1", + "wyrihaximus/react-child-process-promise": "^2.0.2", "wyrihaximus/ticking-promise": "^1.5" }, "require-dev": { @@ -824,7 +829,48 @@ "email": "ceesjank@gmail.com" } ], - "time": "2018-02-24T17:44:46+00:00" + "description": "Detect CPU core count and assign tasks to a specific code", + "time": "2019-01-19T21:54:34+00:00" + }, + { + "name": "wyrihaximus/file-descriptors", + "version": "0.1.0", + "source": { + "type": "git", + "url": "https://github.com/WyriHaximus/php-file-descriptors.git", + "reference": "0a1b5e95fb0bcefd18a012f0faa1243e9af4ef7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WyriHaximus/php-file-descriptors/zipball/0a1b5e95fb0bcefd18a012f0faa1243e9af4ef7c", + "reference": "0a1b5e95fb0bcefd18a012f0faa1243e9af4ef7c", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^5.4", + "tivie/php-os-detector": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "WyriHaximus\\FileDescriptors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + } + ], + "description": "List open file descriptors for the current process cross platform", + "time": "2019-01-15T07:20:48+00:00" }, { "name": "wyrihaximus/json-throwable", @@ -874,16 +920,16 @@ }, { "name": "wyrihaximus/react-child-process-messenger", - "version": "2.8.1", + "version": "2.9.3", "source": { "type": "git", "url": "https://github.com/WyriHaximus/reactphp-child-process-messenger.git", - "reference": "afde926a729868f5d6f64f378836123c3b247dea" + "reference": "960fcaa4922d56ddb23e2e187a9c8b8f62b7a73e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-messenger/zipball/afde926a729868f5d6f64f378836123c3b247dea", - "reference": "afde926a729868f5d6f64f378836123c3b247dea", + "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-messenger/zipball/960fcaa4922d56ddb23e2e187a9c8b8f62b7a73e", + "reference": "960fcaa4922d56ddb23e2e187a9c8b8f62b7a73e", "shasum": "" }, "require": { @@ -891,20 +937,22 @@ "doctrine/inflector": "^1.0", "evenement/evenement": "^3.0 || ^2.0 || ^1.0", "indigophp/hash-compat": "^1.0", - "paragonie/random_compat": "^2.0", - "php": "^5.4||^7.0", - "react/child-process": "^0.5 || ^0.4", + "paragonie/random_compat": "^9.0 || ^2.0", + "php": "^7.0 || ^5.4", + "react/child-process": "^0.6 || ^0.5 | ^0.4", "react/promise": "^2.2", "react/promise-stream": "^1.1", - "react/socket": "^0.8.1", + "react/promise-timer": "^1.5", + "react/socket": "^1.0 || ^0.8.1", + "wyrihaximus/file-descriptors": "^1.0 || ^0.1", "wyrihaximus/json-throwable": "^2.0 || ^1.1.1", "wyrihaximus/ticking-promise": "^1.4" }, "require-dev": { "clue/block-react": "^1.2", "friendsofphp/php-cs-fixer": "^2.2", - "jakub-onderka/php-console-highlighter": "^0.3.2", - "jakub-onderka/php-parallel-lint": "^0.9.2", + "jakub-onderka/php-console-highlighter": "^0.4", + "jakub-onderka/php-parallel-lint": "^1.0.0", "phpunit/phpunit": "^4.8.35||^5.0||^6.0" }, "type": "library", @@ -925,34 +973,36 @@ } ], "description": "Messenger decorator for react/child-process", - "time": "2018-03-23T21:26:12+00:00" + "time": "2019-03-26T20:05:48+00:00" }, { "name": "wyrihaximus/react-child-process-pool", - "version": "1.4.2", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/WyriHaximus/reactphp-child-process-pool.git", - "reference": "232f2aef55ce6df816f837442e79ff2976ebc8ed" + "reference": "d61e27af86e76e2e1045a262a2f4ab43929e521e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-pool/zipball/232f2aef55ce6df816f837442e79ff2976ebc8ed", - "reference": "232f2aef55ce6df816f837442e79ff2976ebc8ed", + "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-pool/zipball/d61e27af86e76e2e1045a262a2f4ab43929e521e", + "reference": "d61e27af86e76e2e1045a262a2f4ab43929e521e", "shasum": "" }, "require": { "evenement/evenement": "^3.0 || ^2.0", - "php": "^5.4||^7.0", - "wyrihaximus/cpu-core-detector": "^1.0.0", - "wyrihaximus/react-child-process-messenger": "^2.7.1", + "php": "^7.0 || ^5.4", + "react/event-loop": "^1.1", + "wyrihaximus/cpu-core-detector": "^1.0.2", + "wyrihaximus/file-descriptors": "^1.0 || ^0.1", + "wyrihaximus/react-child-process-messenger": "^2.9", "wyrihaximus/ticking-promise": "^1.5" }, "require-dev": { - "clue/block-react": "^1.1", + "clue/block-react": "^1.3", "phake/phake": "^2.2.1", "phpunit/phpunit": "^4.8.35||^5.0", - "squizlabs/php_codesniffer": "^1.5.6", + "squizlabs/php_codesniffer": "^3.3.2", "vectorface/dunit": "~2.0" }, "suggest": { @@ -977,32 +1027,32 @@ "email": "ceesjank@gmail.com" } ], - "time": "2018-04-23T17:21:00+00:00" + "description": "Pool wyrihaximus/react-child-process-messenger processes", + "time": "2019-03-20T21:11:00+00:00" }, { "name": "wyrihaximus/react-child-process-promise", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/WyriHaximus/reactphp-child-process-promise.git", - "reference": "9b6f1bace7af43afc79fa85b91c793cdb3ff199b" + "reference": "206fe3d5180c7b6f757e8aaa9fef687f42d43164" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-promise/zipball/9b6f1bace7af43afc79fa85b91c793cdb3ff199b", - "reference": "9b6f1bace7af43afc79fa85b91c793cdb3ff199b", + "url": "https://api.github.com/repos/WyriHaximus/reactphp-child-process-promise/zipball/206fe3d5180c7b6f757e8aaa9fef687f42d43164", + "reference": "206fe3d5180c7b6f757e8aaa9fef687f42d43164", "shasum": "" }, "require": { "php": "^5.4||^7.0", - "react/child-process": "^0.5 || ^0.4", - "react/promise": "^2.2", + "react/child-process": "^0.6 || ^0.5 || ^0.4", + "react/promise": "^2.7", "wyrihaximus/ticking-promise": "^1.5.2" }, "require-dev": { - "phake/phake": "^2.1", "phpunit/phpunit": "^4.4||^5.0", - "squizlabs/php_codesniffer": "^1.5.6", + "squizlabs/php_codesniffer": "^3.3.2", "vectorface/dunit": "^2.0" }, "type": "library", @@ -1026,7 +1076,7 @@ } ], "description": "Wrapping ticks into a promise", - "time": "2017-10-17T11:37:15+00:00" + "time": "2019-01-18T21:42:17+00:00" }, { "name": "wyrihaximus/ticking-promise", @@ -1079,31 +1129,31 @@ "packages-dev": [ { "name": "clue/block-react", - "version": "v1.2.0", + "version": "v1.3.1", "source": { "type": "git", - "url": "https://github.com/clue/php-block-react.git", - "reference": "966c255580ec7a0259338798ddb89f77e121fe9e" + "url": "https://github.com/clue/reactphp-block.git", + "reference": "2f516b28259c203d67c4c963772dd7e9db652737" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/clue/php-block-react/zipball/966c255580ec7a0259338798ddb89f77e121fe9e", - "reference": "966c255580ec7a0259338798ddb89f77e121fe9e", + "url": "https://api.github.com/repos/clue/reactphp-block/zipball/2f516b28259c203d67c4c963772dd7e9db652737", + "reference": "2f516b28259c203d67c4c963772dd7e9db652737", "shasum": "" }, "require": { "php": ">=5.3", "react/event-loop": "^1.0 || ^0.5 || ^0.4 || ^0.3.5", - "react/promise": "~2.1|~1.2", - "react/promise-timer": "~1.0" + "react/promise": "^2.7 || ^1.2.1", + "react/promise-timer": "^1.5" }, "require-dev": { - "phpunit/phpunit": "^5.0 || ^4.8" + "phpunit/phpunit": "^6.4 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { "files": [ - "src/functions.php" + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1116,17 +1166,19 @@ "email": "christian@lueck.tv" } ], - "description": "Integrate async React PHP components into your blocking environment", - "homepage": "https://github.com/clue/php-block-react", + "description": "Lightweight library that eases integrating async components built for ReactPHP in a traditional, blocking environment.", + "homepage": "https://github.com/clue/reactphp-block", "keywords": [ "async", + "await", "blocking", "event loop", "promise", "reactphp", + "sleep", "synchronous" ], - "time": "2017-08-03T13:14:15+00:00" + "time": "2019-04-09T11:45:04+00:00" }, { "name": "phpdocumentor/reflection-docblock", @@ -1179,33 +1231,33 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.5", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.8.x-dev" } }, "autoload": { @@ -1238,7 +1290,7 @@ "spy", "stub" ], - "time": "2018-02-19T10:16:54+00:00" + "time": "2018-08-05T17:53:17+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1614,6 +1666,7 @@ "mock", "xunit" ], + "abandoned": true, "time": "2015-10-02T06:51:40+00:00" }, { @@ -1988,22 +2041,81 @@ "homepage": "https://github.com/sebastianbergmann/version", "time": "2015-06-21T13:59:46+00:00" }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "82ebae02209c21113908c229e9883c419720738a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", + "reference": "82ebae02209c21113908c229e9883c419720738a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2019-02-06T07:57:58+00:00" + }, { "name": "symfony/yaml", - "version": "v2.8.36", + "version": "v2.8.50", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "be720fcfae4614df204190d57795351059946a77" + "reference": "02c1859112aa779d9ab394ae4f3381911d84052b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/be720fcfae4614df204190d57795351059946a77", - "reference": "be720fcfae4614df204190d57795351059946a77", + "url": "https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b", + "reference": "02c1859112aa779d9ab394ae4f3381911d84052b", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { @@ -2035,7 +2147,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-01-03T07:36:31+00:00" + "time": "2018-11-11T11:18:13+00:00" } ], "aliases": [], diff --git a/travis-init.sh b/travis-init.sh index 485ebd98..41a328bf 100755 --- a/travis-init.sh +++ b/travis-init.sh @@ -13,6 +13,16 @@ fi #set -e #set -o pipefail +# install libuv + ext-uv on PHP7 +if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && + "$TRAVIS_PHP_VERSION" != "hhvm-nightly" && + "$TRAVIS_PHP_VERSION" != "5.4" && + "$TRAVIS_PHP_VERSION" != "5.5" && + "$TRAVIS_PHP_VERSION" != "5.6" ]]; then + sudo apt-get install -y libuv1 libuv1-dev + yes | pecl install uv-beta +fi + #if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && # "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]]; then From 594d10985544da027625443aaa0a4893cd91a388 Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 14:00:06 +0200 Subject: [PATCH 03/10] PHP7 --- .travis.yml | 7 ++----- travis-init.sh | 17 +++++------------ 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 66fe7bd9..6b3d95ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,18 +5,15 @@ cache: - $HOME/.composer/cache/files php: - - 5.4 - - 5.5 - - 5.6 - 7.0 - 7.1 - 7.2 + - 7.3 - nightly matrix: allow_failures: - - php: 5.4 - - php: 7.1 + - php: nightly install: - ./travis-init.sh diff --git a/travis-init.sh b/travis-init.sh index 41a328bf..4b7d85fa 100755 --- a/travis-init.sh +++ b/travis-init.sh @@ -13,18 +13,11 @@ fi #set -e #set -o pipefail -# install libuv + ext-uv on PHP7 if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && - "$TRAVIS_PHP_VERSION" != "hhvm-nightly" && - "$TRAVIS_PHP_VERSION" != "5.4" && - "$TRAVIS_PHP_VERSION" != "5.5" && - "$TRAVIS_PHP_VERSION" != "5.6" ]]; then - sudo apt-get install -y libuv1 libuv1-dev - yes | pecl install uv-beta -fi - -#if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && -# "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]]; then + "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]]; then + # install libuv + ext-uv + sudo apt-get install -y libuv1 libuv1-dev + yes | pecl install uv-beta # install "libevent" (used by 'event' and 'libevent' PHP extensions) # sudo apt-get install -y libevent-dev @@ -56,4 +49,4 @@ fi # echo "extension=libev.so" >> "$(php -r 'echo php_ini_loaded_file();')" # fi -#fi +fi From 541f364da3eb769d80b3b10fa57b54e8ffc1ab9a Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 14:05:48 +0200 Subject: [PATCH 04/10] Build from source --- travis-init.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/travis-init.sh b/travis-init.sh index 4b7d85fa..1e045d0d 100755 --- a/travis-init.sh +++ b/travis-init.sh @@ -15,8 +15,10 @@ fi if [[ "$TRAVIS_PHP_VERSION" != "hhvm" && "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]]; then - # install libuv + ext-uv - sudo apt-get install -y libuv1 libuv1-dev + # travis does not seem to have libuv1 ? + git clone https://github.com/libuv/libuv && cd libuv && git checkout tags/v1.28.0 && sh autogen.sh && ./configure && make && sudo make install && cd .. + + # install ext-uv yes | pecl install uv-beta # install "libevent" (used by 'event' and 'libevent' PHP extensions) From d82a870d9ed30f3650885a550ad2cf91b6d7659b Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 14:11:15 +0200 Subject: [PATCH 05/10] Only use uv with ext-uv event loop --- src/Filesystem.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Filesystem.php b/src/Filesystem.php index e5c5d458..dea333a4 100644 --- a/src/Filesystem.php +++ b/src/Filesystem.php @@ -4,6 +4,7 @@ use RuntimeException; use React\EventLoop\LoopInterface; +use React\EventLoop\ExtUvLoop; use React\Filesystem\Node; class Filesystem implements FilesystemInterface @@ -23,8 +24,12 @@ public static function create(LoopInterface $loop, array $options = []) { $adapters = static::getSupportedAdapters(); - if (!empty($adapters)) { - $adapter = "\\React\\Filesystem\\".$adapters[0]."\\Adapter"; + foreach($adapters as $adapter) { + if($adapter === 'Uv' && !$loop instanceof ExtUvLoop) { + continue; + } + + $adapter = "\\React\\Filesystem\\".$adapters[$i]."\\Adapter"; return static::setFilesystemOnAdapter(static::createFromAdapter(new $adapter($loop, $options))); } From 678cbb4f9994b47d614875130bfc996cf48806d6 Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 14:11:46 +0200 Subject: [PATCH 06/10] Oops --- src/Filesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Filesystem.php b/src/Filesystem.php index dea333a4..20e66211 100644 --- a/src/Filesystem.php +++ b/src/Filesystem.php @@ -29,7 +29,7 @@ public static function create(LoopInterface $loop, array $options = []) continue; } - $adapter = "\\React\\Filesystem\\".$adapters[$i]."\\Adapter"; + $adapter = "\\React\\Filesystem\\".$adapter."\\Adapter"; return static::setFilesystemOnAdapter(static::createFromAdapter(new $adapter($loop, $options))); } From 620c940f11b81ad7d30c8e5a7e6f396767ae9125 Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 15:06:24 +0200 Subject: [PATCH 07/10] Things --- tests/Adapters/AbstractAdaptersTest.php | 30 +++++++++++++++++++------ tests/ChildProcess/AdapterTest.php | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/tests/Adapters/AbstractAdaptersTest.php b/tests/Adapters/AbstractAdaptersTest.php index d9127ea3..a0a79e67 100644 --- a/tests/Adapters/AbstractAdaptersTest.php +++ b/tests/Adapters/AbstractAdaptersTest.php @@ -6,7 +6,7 @@ use React\Filesystem\ChildProcess; use React\Filesystem\Eio; use React\Filesystem\Filesystem; -use React\Filesystem\Pthreads; +use React\Filesystem\Uv; use React\Tests\Filesystem\TestCase; use WyriHaximus\React\ChildProcess\Pool\Options; @@ -21,6 +21,13 @@ public function adapterProvider() { $adapters = []; + if (function_exists('uv_loop_new')) + { + $this->adapterFactory($adapters, 'libuv', function () { + return new EventLoop\ExtUvLoop(); + }); + } + if (function_exists('event_base_new')) { $this->adapterFactory($adapters, 'libevent', function () { @@ -55,15 +62,15 @@ public function adapterProvider() protected function adapterFactory(&$adapters, $loopSlug, callable $loopFactory) { - $adapters[$loopSlug . '-factory'] = $this->getFacoryProvider($loopFactory); + $adapters[$loopSlug . '-factory'] = $this->getFactoryProvider($loopFactory); $adapters[$loopSlug . '-child-process'] = $this->getChildProcessProvider($loopFactory); - if (extension_loaded('eio')) { - $adapters[$loopSlug . '-eio'] = $this->getEioProvider($loopFactory); + if (extension_loaded('uv')) { + $adapters[$loopSlug . '-uv'] = $this->getUvProvider($loopFactory); } - if (extension_loaded('pthreads')) { - $adapters[$loopSlug . '-pthreads'] = $this->getPthreadsProvider($loopFactory); + if (extension_loaded('eio')) { + $adapters[$loopSlug . '-eio'] = $this->getEioProvider($loopFactory); } } @@ -85,7 +92,16 @@ protected function getEioProvider(callable $loopFactory) ]; } - protected function getFacoryProvider(callable $loopFactory) + protected function getUvProvider(callable $loopFactory) + { + $loop = $loopFactory(); + return [ + $loop, + new Uv\Adapter($loop), + ]; + } + + protected function getFactoryProvider(callable $loopFactory) { $loop = $loopFactory(); return [ diff --git a/tests/ChildProcess/AdapterTest.php b/tests/ChildProcess/AdapterTest.php index 4ca0ef88..114607be 100644 --- a/tests/ChildProcess/AdapterTest.php +++ b/tests/ChildProcess/AdapterTest.php @@ -391,7 +391,7 @@ public function testLsStream() $calledOnData = false; $stream->on('data', function (NodeInterface $file) use (&$calledOnData) { $this->assertInstanceOf('React\Filesystem\Node\File', $file); - $this->assertSame('foo.bar/bar.foo', $file->getPath()); + $this->assertSame('foo.bar' . DIRECTORY_SEPARATOR . 'bar.foo', $file->getPath()); $calledOnData = true; }); From 75e72b1914e2b420a257d8341899fd88288e1cc8 Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 15:19:35 +0200 Subject: [PATCH 08/10] Just uv things --- tests/Adapters/AbstractAdaptersTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Adapters/AbstractAdaptersTest.php b/tests/Adapters/AbstractAdaptersTest.php index a0a79e67..a8b7aefe 100644 --- a/tests/Adapters/AbstractAdaptersTest.php +++ b/tests/Adapters/AbstractAdaptersTest.php @@ -65,7 +65,7 @@ protected function adapterFactory(&$adapters, $loopSlug, callable $loopFactory) $adapters[$loopSlug . '-factory'] = $this->getFactoryProvider($loopFactory); $adapters[$loopSlug . '-child-process'] = $this->getChildProcessProvider($loopFactory); - if (extension_loaded('uv')) { + if (extension_loaded('uv') && $loopFactory() instanceof EventLoop\ExtUvLoop) { $adapters[$loopSlug . '-uv'] = $this->getUvProvider($loopFactory); } From 7967f0081663e7d427ed3d953198bbdb352ec69d Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 15:49:38 +0200 Subject: [PATCH 09/10] Testing things with paths:tm: --- tests/AdapterTestAbstract.php | 6 +----- tests/Adapters/AbstractAdaptersTest.php | 14 +++++++------- tests/ChildProcess/AdapterTest.php | 4 ++-- tests/FilesystemTest.php | 7 ++++--- tests/Node/DirectoryTest.php | 8 ++++---- tests/Node/GenericOperationTraitTest.php | 17 +++++++++-------- tests/TestCase.php | 11 +++++++++++ tests/Uv/AdapterTest.php | 9 +++++++++ 8 files changed, 47 insertions(+), 29 deletions(-) diff --git a/tests/AdapterTestAbstract.php b/tests/AdapterTestAbstract.php index 36a4f491..930be5bb 100644 --- a/tests/AdapterTestAbstract.php +++ b/tests/AdapterTestAbstract.php @@ -108,7 +108,7 @@ public function testChmod() */ public function testChown() { - if(DIRECTORY_SEPARATOR === '\\') { + if(\DIRECTORY_SEPARATOR === '\\') { return $this->markTestSkipped('Unsupported on Windows'); } @@ -125,10 +125,6 @@ public function testChown() public function testStat() { - if(DIRECTORY_SEPARATOR === '\\') { - return $this->markTestSkipped('Unsupported on Windows (different dev, rdev and ino)'); - } - $path = $this->tmpDir . \uniqid('', true); $this->assertTrue(\touch($path, \time(), \time())); diff --git a/tests/Adapters/AbstractAdaptersTest.php b/tests/Adapters/AbstractAdaptersTest.php index a8b7aefe..aab16eec 100644 --- a/tests/Adapters/AbstractAdaptersTest.php +++ b/tests/Adapters/AbstractAdaptersTest.php @@ -21,13 +21,6 @@ public function adapterProvider() { $adapters = []; - if (function_exists('uv_loop_new')) - { - $this->adapterFactory($adapters, 'libuv', function () { - return new EventLoop\ExtUvLoop(); - }); - } - if (function_exists('event_base_new')) { $this->adapterFactory($adapters, 'libevent', function () { @@ -42,6 +35,13 @@ public function adapterProvider() }); } + if (function_exists('uv_loop_new')) + { + $this->adapterFactory($adapters, 'extuv', function () { + return new EventLoop\ExtUvLoop(); + }); + } + if (class_exists('EventBase', false)) { $this->adapterFactory($adapters, 'extevent', function () { diff --git a/tests/ChildProcess/AdapterTest.php b/tests/ChildProcess/AdapterTest.php index 114607be..cd6ec430 100644 --- a/tests/ChildProcess/AdapterTest.php +++ b/tests/ChildProcess/AdapterTest.php @@ -352,7 +352,7 @@ public function testLs() $nodes = $this->await($promise, $loop); - $this->assertEquals(new File('foo.bar/bar.foo', $fs), reset($nodes)); + $this->assertEquals(new File($this->concatPath('foo.bar', 'bar.foo'), $fs), reset($nodes)); } public function testLsStream() @@ -391,7 +391,7 @@ public function testLsStream() $calledOnData = false; $stream->on('data', function (NodeInterface $file) use (&$calledOnData) { $this->assertInstanceOf('React\Filesystem\Node\File', $file); - $this->assertSame('foo.bar' . DIRECTORY_SEPARATOR . 'bar.foo', $file->getPath()); + $this->assertSame($this->concatPath('foo.bar', 'bar.foo'), $file->getPath()); $calledOnData = true; }); diff --git a/tests/FilesystemTest.php b/tests/FilesystemTest.php index 3fed619c..0316e56f 100644 --- a/tests/FilesystemTest.php +++ b/tests/FilesystemTest.php @@ -2,6 +2,7 @@ namespace React\Tests\Filesystem; +use React\EventLoop\Factory; use React\Filesystem\Filesystem; use React\Filesystem\InstantInvoker; use React\Promise\FulfilledPromise; @@ -13,7 +14,7 @@ public function testCreate() { $this->assertInstanceOf( 'React\Filesystem\Filesystem', - Filesystem::create($this->getMock('React\EventLoop\LoopInterface'), [ + Filesystem::create(Factory::create(), [ 'pool' => [ 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', ], @@ -45,7 +46,7 @@ public function testFactory() public function testFile() { - $file = Filesystem::create($this->getMock('React\EventLoop\LoopInterface'), [ + $file = Filesystem::create(Factory::create(), [ 'pool' => [ 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', ], @@ -56,7 +57,7 @@ public function testFile() public function testDir() { - $directory = Filesystem::create($this->getMock('React\EventLoop\LoopInterface'), [ + $directory = Filesystem::create(Factory::create(), [ 'pool' => [ 'class' => 'WyriHaximus\React\ChildProcess\Pool\Pool\Dummy', ], diff --git a/tests/Node/DirectoryTest.php b/tests/Node/DirectoryTest.php index 34a8c8a4..0da6a6fb 100644 --- a/tests/Node/DirectoryTest.php +++ b/tests/Node/DirectoryTest.php @@ -22,7 +22,7 @@ public function providerToString() return [ [ 'foo.bar', - 'foo.bar/', + 'foo.bar' . \DIRECTORY_SEPARATOR, ], ]; } @@ -313,21 +313,21 @@ public function testCopyStreamingABC() $adapter ->expects($this->at(0)) ->method('stat') - ->with('bar.foo/foo.bar/') + ->with($this->concatPath('bar.foo', 'foo.bar', '')) ->will($this->returnValue(new RejectedPromise())) ; $adapter ->expects($this->at(1)) ->method('stat') - ->with('bar.foo/') + ->with('bar.foo' . \DIRECTORY_SEPARATOR) ->will($this->returnValue(new FulfilledPromise())) ; $adapter ->expects($this->at(3)) ->method('stat') - ->with('bar.foo/foo.bar/') + ->with($this->concatPath('bar.foo', 'foo.bar', '')) ->will($this->returnValue(new FulfilledPromise())) ; diff --git a/tests/Node/GenericOperationTraitTest.php b/tests/Node/GenericOperationTraitTest.php index 09dde74c..3e6f46fa 100644 --- a/tests/Node/GenericOperationTraitTest.php +++ b/tests/Node/GenericOperationTraitTest.php @@ -112,36 +112,37 @@ public function testChownDefaults() public function testCreateNameNParentFromFilename() { - $node = new File('/foo/bar/baz/rabbit/kitten/index.php', Filesystem::createFromAdapter($this->mockAdapter())); + $path = $this->concatPath('', 'foo', 'bar', 'baz', 'rabbit', 'kitten', 'index.php'); + $node = new File($path, Filesystem::createFromAdapter($this->mockAdapter())); foreach ([ [ 'index.php', - '/foo/bar/baz/rabbit/kitten/index.php', + $path, ], [ 'kitten', - '/foo/bar/baz/rabbit/kitten/', + $this->concatPath('', 'foo', 'bar', 'baz', 'rabbit', 'kitten', ''), ], [ 'rabbit', - '/foo/bar/baz/rabbit/', + $this->concatPath('', 'foo', 'bar', 'baz', 'rabbit', ''), ], [ 'baz', - '/foo/bar/baz/', + $this->concatPath('', 'foo', 'bar', 'baz', ''), ], [ 'bar', - '/foo/bar/', + $this->concatPath('', 'foo', 'bar', ''), ], [ 'foo', - '/foo/', + $this->concatPath('', 'foo', ''), ], [ '', - '/', + \DIRECTORY_SEPARATOR, ], ] as $names) { $this->assertSame($names[0], $node->getName()); diff --git a/tests/TestCase.php b/tests/TestCase.php index 06c75036..a7764be0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -118,4 +118,15 @@ protected function await(PromiseInterface $promise, LoopInterface $loop, $timeou $loop->run(); // Ensure we let the loop run it's course to clean up return $result; } + + protected function concatPath(string ...$parts) + { + $path = ''; + + foreach ($parts as $part) { + $path .= $part . \DIRECTORY_SEPARATOR; + } + + return \substr($path, 0, -1); + } } diff --git a/tests/Uv/AdapterTest.php b/tests/Uv/AdapterTest.php index d825a1b0..86265c55 100644 --- a/tests/Uv/AdapterTest.php +++ b/tests/Uv/AdapterTest.php @@ -19,4 +19,13 @@ public function createAdapter() { return (new Adapter($this->loop)); } + + public function testStat() + { + if (\DIRECTORY_SEPARATOR === '\\') { + return $this->markTestSkipped('Unsupported on Windows (different dev, rdev and ino)'); + } + + parent::testStat(); + } } From aaf564e7b2a7a0f4f6f386ab94f619e448306af5 Mon Sep 17 00:00:00 2001 From: Charlotte Dunois Date: Sat, 4 May 2019 16:52:34 +0200 Subject: [PATCH 10/10] Things --- src/Uv/Adapter.php | 16 ++++++++++------ tests/Adapters/AbstractAdaptersTest.php | 11 ++++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Uv/Adapter.php b/src/Uv/Adapter.php index 787d3c85..3716351c 100644 --- a/src/Uv/Adapter.php +++ b/src/Uv/Adapter.php @@ -151,11 +151,15 @@ public function callFilesystem($function, $args, $callable = null) $args[] = function (...$args) use ($deferred, $callable) { $this->unregister(); - try { - $deferred->resolve(\call_user_func_array($callable, $args)); - } catch (\Throwable $e) { - $deferred->reject($e); - } + // Without a future tick it's very likely that the underlying + // uv loop handle we try to use got garbage collected + $this->loop->futureTick(function () use ($args, $deferred, $callable) { + try { + $deferred->resolve(\call_user_func_array($callable, $args)); + } catch (\Throwable $e) { + $deferred->reject($e); + } + }); }; try { @@ -297,7 +301,7 @@ public function lsStream($path) if ($bool !== true) { throw new Exception('Unable to list the directory'); } - + $this->processLsContents($path, $result, $stream); }); diff --git a/tests/Adapters/AbstractAdaptersTest.php b/tests/Adapters/AbstractAdaptersTest.php index aab16eec..f641976c 100644 --- a/tests/Adapters/AbstractAdaptersTest.php +++ b/tests/Adapters/AbstractAdaptersTest.php @@ -2,6 +2,7 @@ namespace React\Tests\Filesystem\Adapters; +use RuntimeException; use React\EventLoop; use React\Filesystem\ChildProcess; use React\Filesystem\Eio; @@ -62,7 +63,15 @@ public function adapterProvider() protected function adapterFactory(&$adapters, $loopSlug, callable $loopFactory) { - $adapters[$loopSlug . '-factory'] = $this->getFactoryProvider($loopFactory); + try { + $adapters[$loopSlug . '-factory'] = $this->getFactoryProvider($loopFactory); + } catch (RuntimeException $e) { + /* + * Ignore exception. This would happen if we use + * a non-uv loop and no compatible adapters were found. + */ + } + $adapters[$loopSlug . '-child-process'] = $this->getChildProcessProvider($loopFactory); if (extension_loaded('uv') && $loopFactory() instanceof EventLoop\ExtUvLoop) {