2018-05-22 23:23:20 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace Wikimedia\ParamValidator\Util;
|
|
|
|
|
|
|
|
|
|
require_once __DIR__ . '/UploadedFileTestBase.php';
|
|
|
|
|
|
|
|
|
|
use RuntimeException;
|
2020-04-25 12:35:10 +00:00
|
|
|
use TypeError;
|
2018-05-22 23:23:20 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers Wikimedia\ParamValidator\Util\UploadedFileStream
|
|
|
|
|
*/
|
|
|
|
|
class UploadedFileStreamTest extends UploadedFileTestBase {
|
|
|
|
|
|
|
|
|
|
public function testConstruct_doesNotExist() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
unlink( $filename );
|
|
|
|
|
|
2021-03-25 10:38:39 +00:00
|
|
|
$this->assertFileDoesNotExist( $filename, 'Non existence check' );
|
2019-10-11 22:22:26 +00:00
|
|
|
$this->expectException( RuntimeException::class );
|
|
|
|
|
$this->expectExceptionMessage( "Failed to open file:" );
|
2018-05-22 23:23:20 +00:00
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testConstruct_notReadable() {
|
2020-10-30 19:02:23 +00:00
|
|
|
if ( wfIsWindows() ) {
|
|
|
|
|
$this->markTestSkipped( "Skip test, since chmod does not work on windows" );
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-22 23:23:20 +00:00
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
|
|
|
|
|
chmod( $filename, 0000 );
|
2019-10-11 22:22:26 +00:00
|
|
|
$this->expectException( RuntimeException::class );
|
|
|
|
|
$this->expectExceptionMessage( "Failed to open file:" );
|
2018-05-22 23:23:20 +00:00
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testCloseOnDestruct() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
$fp = TestingAccessWrapper::newFromObject( $stream )->fp;
|
2021-11-21 16:23:11 +00:00
|
|
|
$this->assertSame( 'f', fread( $fp, 1 ), 'read check' );
|
2018-05-22 23:23:20 +00:00
|
|
|
unset( $stream );
|
2020-04-25 12:35:10 +00:00
|
|
|
try {
|
|
|
|
|
// PHP 7 raises warnings
|
2022-02-24 19:57:59 +00:00
|
|
|
$this->assertFalse( @fread( $fp, 1 ) );
|
2020-04-25 12:35:10 +00:00
|
|
|
} catch ( TypeError $ex ) {
|
|
|
|
|
// PHP 8 throws
|
|
|
|
|
}
|
2018-05-22 23:23:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testToString() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
|
|
|
|
|
// Always starts at the start of the stream
|
|
|
|
|
$stream->seek( 3 );
|
|
|
|
|
$this->assertSame( 'foobar', (string)$stream );
|
|
|
|
|
|
|
|
|
|
// No exception when closed
|
|
|
|
|
$stream->close();
|
|
|
|
|
$this->assertSame( '', (string)$stream );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testToString_Error() {
|
|
|
|
|
if ( !class_exists( \Error::class ) ) {
|
|
|
|
|
$this->markTestSkipped( 'No PHP Error class' );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ... Yeah
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = $this->getMockBuilder( UploadedFileStream::class )
|
|
|
|
|
->setConstructorArgs( [ $filename ] )
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'getContents' ] )
|
2018-05-22 23:23:20 +00:00
|
|
|
->getMock();
|
2021-02-07 13:10:36 +00:00
|
|
|
$stream->method( 'getContents' )->willReturnCallback( static function () {
|
2018-05-22 23:23:20 +00:00
|
|
|
throw new \Error( 'Bogus' );
|
|
|
|
|
} );
|
|
|
|
|
$this->assertSame( '', (string)$stream );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testClose() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
|
|
|
|
|
$stream->close();
|
|
|
|
|
|
|
|
|
|
// Second call doesn't error
|
|
|
|
|
$stream->close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testDetach() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
|
|
|
|
|
// We got the file descriptor
|
|
|
|
|
$fp = $stream->detach();
|
|
|
|
|
$this->assertNotNull( $fp );
|
|
|
|
|
$this->assertSame( 'f', fread( $fp, 1 ) );
|
|
|
|
|
|
|
|
|
|
// Stream operations now fail.
|
|
|
|
|
try {
|
|
|
|
|
$stream->seek( 0 );
|
|
|
|
|
} catch ( \PHPUnit\Framework\AssertionFailedError $ex ) {
|
|
|
|
|
throw $ex;
|
|
|
|
|
} catch ( RuntimeException $ex ) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stream close doesn't affect the file descriptor
|
|
|
|
|
$stream->close();
|
|
|
|
|
$this->assertSame( 'o', fread( $fp, 1 ) );
|
|
|
|
|
|
|
|
|
|
// Stream destruction doesn't affect the file descriptor
|
|
|
|
|
unset( $stream );
|
|
|
|
|
$this->assertSame( 'o', fread( $fp, 1 ) );
|
|
|
|
|
|
|
|
|
|
// On a closed stream, we don't get a file descriptor
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
$stream->close();
|
|
|
|
|
$this->assertNull( $stream->detach() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetSize() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
file_put_contents( $filename, 'foobarbaz' );
|
|
|
|
|
$this->assertSame( 9, $stream->getSize() );
|
|
|
|
|
|
|
|
|
|
// Cached
|
|
|
|
|
file_put_contents( $filename, 'baz' );
|
|
|
|
|
clearstatcache();
|
2021-11-21 16:23:11 +00:00
|
|
|
$this->assertSame( 3, stat( $filename )['size'], 'size check' );
|
2018-05-22 23:23:20 +00:00
|
|
|
$this->assertSame( 9, $stream->getSize() );
|
|
|
|
|
|
|
|
|
|
// No error if closed
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
$stream->close();
|
|
|
|
|
$this->assertSame( null, $stream->getSize() );
|
|
|
|
|
|
|
|
|
|
// No error even if the fd goes bad
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
fclose( TestingAccessWrapper::newFromObject( $stream )->fp );
|
|
|
|
|
$this->assertSame( null, $stream->getSize() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testSeekTell() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
|
|
|
|
|
$stream->seek( 2 );
|
|
|
|
|
$this->assertSame( 2, $stream->tell() );
|
|
|
|
|
$stream->seek( 2, SEEK_CUR );
|
|
|
|
|
$this->assertSame( 4, $stream->tell() );
|
|
|
|
|
$stream->seek( -5, SEEK_END );
|
|
|
|
|
$this->assertSame( 1, $stream->tell() );
|
|
|
|
|
$stream->read( 2 );
|
|
|
|
|
$this->assertSame( 3, $stream->tell() );
|
|
|
|
|
|
|
|
|
|
$stream->close();
|
|
|
|
|
try {
|
|
|
|
|
$stream->seek( 0 );
|
|
|
|
|
} catch ( \PHPUnit\Framework\AssertionFailedError $ex ) {
|
|
|
|
|
throw $ex;
|
|
|
|
|
} catch ( RuntimeException $ex ) {
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
$stream->tell();
|
|
|
|
|
} catch ( \PHPUnit\Framework\AssertionFailedError $ex ) {
|
|
|
|
|
throw $ex;
|
|
|
|
|
} catch ( RuntimeException $ex ) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testEof() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
|
|
|
|
|
$this->assertFalse( $stream->eof() );
|
|
|
|
|
$stream->getContents();
|
|
|
|
|
$this->assertTrue( $stream->eof() );
|
|
|
|
|
$stream->seek( -1, SEEK_END );
|
|
|
|
|
$this->assertFalse( $stream->eof() );
|
|
|
|
|
|
|
|
|
|
// No error if closed
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
$stream->close();
|
|
|
|
|
$this->assertTrue( $stream->eof() );
|
|
|
|
|
|
|
|
|
|
// No error even if the fd goes bad
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
fclose( TestingAccessWrapper::newFromObject( $stream )->fp );
|
2019-10-06 14:12:39 +00:00
|
|
|
$this->assertIsBool( $stream->eof() );
|
2018-05-22 23:23:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testIsFuncs() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
$this->assertTrue( $stream->isSeekable() );
|
|
|
|
|
$this->assertTrue( $stream->isReadable() );
|
|
|
|
|
$this->assertFalse( $stream->isWritable() );
|
|
|
|
|
|
|
|
|
|
$stream->close();
|
|
|
|
|
$this->assertFalse( $stream->isSeekable() );
|
|
|
|
|
$this->assertFalse( $stream->isReadable() );
|
|
|
|
|
$this->assertFalse( $stream->isWritable() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRewind() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
|
|
|
|
|
$stream->seek( 2 );
|
|
|
|
|
$this->assertSame( 2, $stream->tell() );
|
|
|
|
|
$stream->rewind();
|
|
|
|
|
$this->assertSame( 0, $stream->tell() );
|
|
|
|
|
|
|
|
|
|
$stream->close();
|
|
|
|
|
try {
|
|
|
|
|
$stream->rewind();
|
|
|
|
|
} catch ( \PHPUnit\Framework\AssertionFailedError $ex ) {
|
|
|
|
|
throw $ex;
|
|
|
|
|
} catch ( RuntimeException $ex ) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testWrite() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$stream->write( 'foo' );
|
|
|
|
|
} catch ( \PHPUnit\Framework\AssertionFailedError $ex ) {
|
|
|
|
|
throw $ex;
|
|
|
|
|
} catch ( RuntimeException $ex ) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testRead() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
|
|
|
|
|
$this->assertSame( 'foo', $stream->read( 3 ) );
|
|
|
|
|
$this->assertSame( 'bar', $stream->read( 10 ) );
|
|
|
|
|
$this->assertSame( '', $stream->read( 10 ) );
|
|
|
|
|
$stream->rewind();
|
|
|
|
|
$this->assertSame( 'foobar', $stream->read( 10 ) );
|
|
|
|
|
|
|
|
|
|
$stream->close();
|
|
|
|
|
try {
|
|
|
|
|
$stream->read( 1 );
|
|
|
|
|
} catch ( \PHPUnit\Framework\AssertionFailedError $ex ) {
|
|
|
|
|
throw $ex;
|
|
|
|
|
} catch ( RuntimeException $ex ) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetContents() {
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
|
|
|
|
|
$this->assertSame( 'foobar', $stream->getContents() );
|
|
|
|
|
$this->assertSame( '', $stream->getContents() );
|
|
|
|
|
$stream->seek( 3 );
|
|
|
|
|
$this->assertSame( 'bar', $stream->getContents() );
|
|
|
|
|
|
|
|
|
|
$stream->close();
|
|
|
|
|
try {
|
|
|
|
|
$stream->getContents();
|
|
|
|
|
} catch ( \PHPUnit\Framework\AssertionFailedError $ex ) {
|
|
|
|
|
throw $ex;
|
|
|
|
|
} catch ( RuntimeException $ex ) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetMetadata() {
|
|
|
|
|
// Whatever
|
|
|
|
|
$filename = $this->makeTemp( __FUNCTION__ );
|
|
|
|
|
$fp = fopen( $filename, 'r' );
|
|
|
|
|
$expect = stream_get_meta_data( $fp );
|
|
|
|
|
fclose( $fp );
|
|
|
|
|
|
|
|
|
|
$stream = new UploadedFileStream( $filename );
|
|
|
|
|
$this->assertSame( $expect, $stream->getMetadata() );
|
|
|
|
|
foreach ( $expect as $k => $v ) {
|
|
|
|
|
$this->assertSame( $v, $stream->getMetadata( $k ) );
|
|
|
|
|
}
|
|
|
|
|
$this->assertNull( $stream->getMetadata( 'bogus' ) );
|
|
|
|
|
|
|
|
|
|
$stream->close();
|
|
|
|
|
try {
|
|
|
|
|
$stream->getMetadata();
|
|
|
|
|
} catch ( \PHPUnit\Framework\AssertionFailedError $ex ) {
|
|
|
|
|
throw $ex;
|
|
|
|
|
} catch ( RuntimeException $ex ) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|