You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

428 lines
12 KiB

<?php
include_once dirname(__FILE__).'/../archivereader.php';
/**
* Test case for ArchiveReader.
*
* @group archive
*/
class ArchiveReaderTest extends PHPUnit_Framework_TestCase
{
protected $fixturesDir;
protected $testFile;
/**
* This method is called before each test is executed.
*/
protected function setUp()
{
$ds = DIRECTORY_SEPARATOR;
$this->fixturesDir = realpath(dirname(__FILE__).'/fixtures');
$this->testFile = $this->fixturesDir.$ds.'rar'.$ds.'4mb.rar';
}
/**
* We need to be able to seek accurately through the available data and check
* that read requests are not out of bounds. Trying to read past the last byte
* or seeking out of bounds should throw an exception.
*/
public function testHandlesBasicSeekingAndReading()
{
$data = "some sample data for testing\n";
$length = strlen($data);
$archive = new TestArchiveReader;
$archive->data = $data;
$archive->start = 0;
$archive->end = $length - 1;
$archive->length = $length;
// Within bounds
$archive->seek(0);
$this->assertSame(0, $archive->offset);
$archive->seek(3);
$this->assertSame(3, $archive->offset);
$archive->seek($archive->end);
$this->assertSame($archive->end, $archive->offset);
$archive->seek(0);
$this->assertSame(0, $archive->offset);
$read = $archive->read(0);
$this->assertSame(0, $archive->offset);
$this->assertSame('', $read);
$read = $archive->read(1);
$this->assertSame(1, $archive->offset);
$this->assertSame('s', $read);
$read = $archive->read(3);
$this->assertSame(4, $archive->offset);
$this->assertSame('ome', $read);
$read = $archive->read(3);
$this->assertSame(7, $archive->offset);
$this->assertSame(' sa', $read);
$archive->seek(5);
$read = $archive->read(6);
$this->assertSame(11, $archive->offset);
$this->assertSame('sample', $read);
$archive->seek($archive->end);
$read = $archive->read(1);
$this->assertSame($archive->end + 1, $archive->offset);
$this->assertSame("\n", $read);
$archive->seek(0);
$read = $archive->read($length);
$this->assertSame($archive->end + 1, $archive->offset);
$this->assertSame($data, $read);
// Out of bounds
$archive->seek(0);
try {
$archive->seek($archive->end + 5);
} catch (InvalidArgumentException $e) {}
$this->assertSame(0, $archive->offset);
try {
$archive->seek(-1);
} catch (InvalidArgumentException $e) {}
$this->assertSame(0, $archive->offset);
try {
$archive->read($length + 1);
} catch (InvalidArgumentException $e) {}
$this->assertSame(0, $archive->offset);
try {
$archive->seek($archive->end + 1);
$archive->read(1);
} catch (InvalidArgumentException $e) {}
$this->assertSame($archive->end + 1, $archive->offset);
try {
$archive->seek($archive->end);
$read = $archive->read(2);
} catch (InvalidArgumentException $e) {}
$this->assertSame($archive->end, $archive->offset);
try {
$archive->seek($archive->end - 1);
$read = $archive->read(3);
} catch (InvalidArgumentException $e) {}
$this->assertSame($archive->end - 1, $archive->offset);
}
/**
* Files can be streamed directly using the open() method, with any errors
* reported via the public $error property.
*
* @depends testHandlesBasicSeekingAndReading
*/
public function testHandlesFileStreams()
{
$archive = new TestArchiveReader;
$this->assertTrue($archive->open($this->testFile));
$this->assertEmpty($archive->error, $archive->error);
$this->assertSame($this->testFile, $archive->file);
$this->assertTrue(is_resource($archive->handle));
$this->assertTrue($archive->analyzed);
$summary = $archive->getSummary();
$this->assertSame(filesize($this->testFile), $summary['fileSize']);
$this->assertSame(0, $summary['dataSize']);
$archive->close();
$this->assertFalse(is_resource($archive->handle));
$this->assertFalse($archive->open('missingfile'));
$this->assertSame('File does not exist (missingfile)', $archive->error);
}
/**
* Data can be passed directly to the instance via the setData() method, with
* any errors reported via the public $error property.
*
* @depends testHandlesBasicSeekingAndReading
*/
public function testHandlesDataFromMemory()
{
$archive = new TestArchiveReader;
$data = file_get_contents($this->testFile);
$this->assertTrue($archive->setData($data));
$this->assertEmpty($archive->error, $archive->error);
$this->assertTrue($archive->analyzed);
$this->assertSame($data, $archive->data);
$summary = $archive->getSummary();
$this->assertSame(strlen($data), $summary['dataSize']);
$this->assertEmpty($summary['fileSize']);
$archive->close();
$this->assertFalse($archive->setData(''));
$this->assertSame('No data was passed, nothing to analyze', $archive->error);
}
/**
* We should be able to specify the start and end points for the archive analysis
* transparently, with all offsets made relative to the given start point.
*
* @depends testHandlesFileStreams
* @depends testHandlesDataFromMemory
*/
public function testByteRangesCanBeSpecifiedForAnalysis()
{
$data = file_get_contents($this->testFile);
$fsize = filesize($this->testFile);
$archive = new TestArchiveReader;
// Without set ranges
$len = $fsize - 1;
$tell = $len - ($len % $archive->readSize);
$archive->open($this->testFile);
$this->assertSame(0, $archive->start);
$this->assertSame($fsize - 1, $archive->end);
$this->assertSame($tell, $archive->tell());
$archive->setData($data);
$this->assertSame(0, $archive->start);
$this->assertSame($fsize - 1, $archive->end);
$this->assertSame($tell, $archive->tell());
// With set ranges
$ranges = array(array(5, 9), array(0, 99), array(50, 249), array(5, $fsize - 1));
foreach ($ranges as $range)
{
$len = $range[1] - $range[0] + 1;
$tell = $range[0] + ($len - ($len % $archive->readSize));
$archive->open($this->testFile, false, $range);
$this->assertSame($range[0], $archive->start);
$this->assertSame($range[1], $archive->end);
$this->assertSame($tell, $archive->tell(), "Length is: $len, offset is: {$archive->offset}");
$archive->setData($data, false, $range);
$this->assertSame($range[0], $archive->start);
$this->assertSame($range[1], $archive->end);
$this->assertSame($tell, $archive->tell(), "Length is: $len, offset is: {$archive->offset}");
}
}
/**
* We shouldn't be able to set ranges that are out of the bounds of any set
* data or opened file, and we should handle these as errors rather than throw
* exceptions.
*
* @depends testByteRangesCanBeSpecifiedForAnalysis
*/
public function testInvalidByteRangesReturnErrors()
{
$data = file_get_contents($this->testFile);
$archive = new TestArchiveReader;
// Common checks
$range = array(-1, -2);
$regex = '/Start.*end.*positive/';
$this->assertFalse($archive->setData($data, false, $range));
$this->assertRegExp($regex, $archive->error);
$this->assertEmpty($archive->data);
$this->assertFalse($archive->open($this->testFile, false, $range));
$this->assertRegExp($regex, $archive->error);
$this->assertNull($archive->handle);
$range = array(1.5, 3);
$regex = '/Start.*end.*integer/';
$this->assertFalse($archive->setData($data, false, $range));
$this->assertRegExp($regex, $archive->error);
$this->assertEmpty($archive->data);
$this->assertFalse($archive->open($this->testFile, false, $range));
$this->assertRegExp($regex, $archive->error);
$this->assertNull($archive->handle);
$range = array(2, 1);
$regex = '/End.*must be higher than start/';
$this->assertFalse($archive->setData($data, false, $range));
$this->assertRegExp($regex, $archive->error);
$this->assertEmpty($archive->data);
$this->assertFalse($archive->open($this->testFile, false, $range));
$this->assertRegExp($regex, $archive->error);
$this->assertNull($archive->handle);
// Setting data
$archive->setMaxReadBytes(100);
$range = array(1, 105);
$regex = '/range.*is invalid/';
$this->assertFalse($archive->setData($data, false, $range));
$this->assertRegExp($regex, $archive->error);
$this->assertEmpty($archive->data);
$range = array(101, 105);
$this->assertFalse($archive->setData($data, false, $range));
$this->assertRegExp($regex, $archive->error);
$this->assertEmpty($archive->data);
// Opening file
$range = array(0, filesize($this->testFile));
$this->assertFalse($archive->open($this->testFile, false, $range));
$this->assertRegExp($regex, $archive->error);
$this->assertNull($archive->handle);
}
/**
* We should be able to retrieve data from the file/data source using any
* absolute byte range, ignoring any analysis range that has been set.
*
* @depends testInvalidByteRangesReturnErrors
*/
public function testAnyDataCanBeFetchedByByteRange()
{
$file = $this->fixturesDir.'/rar/commented.rar';
$data = file_get_contents($file);
$content = 'file content';
$range = array(146, 157);
$archive = new TestArchiveReader;
// Within bounds
$archive->open($file);
$this->assertSame($content, $archive->getRange($range));
$archive->setData($data);
$this->assertSame($content, $archive->getRange($range));
$archive->open($file, false, array(0, 1));
$this->assertSame($content, $archive->getRange($range));
$archive->setData($data, false, array(0, 1));
$this->assertSame($content, $archive->getRange($range));
// Out of bounds
$archive->open($file);
$archive->getRange(array(0, filesize($file)));
$this->assertRegExp('/range.*is invalid/', $archive->error);
}
/**
* We need to be able to save any stored data to temporary files so that we
* can e.g. extract the archive contents using an external client. These
* temporary files should be deleted on reset or destruct.
*
* @depends testHandlesDataFromMemory
*/
public function testCanSaveDataToTemporaryFiles()
{
$archive = new TestArchiveReader;
$data = file_get_contents($this->testFile);
$archive->setData($data);
$temp = $archive->createTempDataFile();
$name = pathinfo($temp, PATHINFO_FILENAME);
$this->assertArrayHasKey($name, $archive->tempFiles);
$this->assertSame($temp, $archive->tempFiles[$name]);
$this->assertFileExists($temp);
$this->assertSame(strlen($data), filesize($temp));
unset($archive);
$this->assertFileNotExists($temp);
}
/**
* As a variation on strpos, we should be able to return a list of all needle
* positions in a given haystack string, and also handle lists of needles.
*
* @dataProvider providerNeedles
* @group static
*/
public function testCanFindAllNeedlePositionsInAHaystack($needle, $result)
{
$haystack = 'Dog the dog in the dog basket did a log';
$this->assertSame($result, ArchiveReader::strposall($haystack, $needle));
}
/**
* Provides test data for the strposall static method.
*/
public function providerNeedles()
{
return array(
// Single needles
array('D', array(0)),
array('s', array(25)),
array('the dog', array(4, 15)),
array('dog', array(8, 19)),
array('og', array(1, 9, 20, 37)),
array('d', array(8, 19, 30, 32)),
array('g', array(2, 10, 21, 38)),
// Arrays of needles
array(array('d', 'og', 'dog'), array(
1 => array(1), 8 => array(0, 2), 9 => array(1), 19 => array(0, 2),
20 => array(1), 30 => array(0), 32 => array(0), 37 => array(1),
)),
array(array('key1' => 's', 'key2' => 'the', 'key3' => 't'), array(
4 => array('key2', 'key3'),
15 => array('key2', 'key3'),
25 => array('key1'),
28 => array('key3'),
)),
);
}
} // End ArchiveReaderTest
class TestArchiveReader extends ArchiveReader
{
// Abstract method implementations
public function getSummary($full=false)
{
return array(
'fileSize' => $this->fileSize,
'dataSize' => $this->dataSize,
);
}
public function getFileList() {}
public function findMarker() {}
protected function analyze()
{
while ($this->offset < $this->length) try {
$this->read($this->readSize);
} catch(Exception $e) {
break;
}
$this->analyzed = true;
return true;
}
// Added for test convenience
public $analyzed = false;
public $readSize = 3;
// Made public for test convenience
public $handle;
public $data = '';
public $offset = 0;
public $length = 0;
public $start = 0;
public $end = 0;
public $tempFiles = array();
public function seek($pos)
{
parent::seek($pos);
}
public function read($num, $confirm=true)
{
return parent::read($num, $confirm);
}
public function tell()
{
return parent::tell();
}
public function getRange(array $range)
{
return parent::getRange($range);
}
public function createTempDataFile()
{
return parent::createTempDataFile();
}
}