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.
354 lines
12 KiB
354 lines
12 KiB
<?php
|
|
|
|
include_once dirname(__FILE__).'/../rarinfo.php';
|
|
|
|
/**
|
|
* Test case for RarInfo.
|
|
*
|
|
* @group rar
|
|
*/
|
|
class RarInfoTest extends PHPUnit_Framework_TestCase
|
|
{
|
|
protected $fixturesDir;
|
|
|
|
/**
|
|
* This method is called before each test is executed.
|
|
*/
|
|
protected function setUp()
|
|
{
|
|
$this->fixturesDir = realpath(dirname(__FILE__).'/fixtures/rar');
|
|
}
|
|
|
|
/**
|
|
* RAR files consist of a series of header blocks and optional bodies for
|
|
* certain block types and subblocks. We should be abe to report an accurate
|
|
* list of all blocks in summmary form.
|
|
*
|
|
* @dataProvider providerTestFixtures
|
|
* @param string $filename sample rar filename
|
|
* @param string $blocks expected list of valid blocks
|
|
*/
|
|
public function testStoresListOfAllValidBlocks($filename, $blocks)
|
|
{
|
|
$rar = new RarInfo;
|
|
$rar->open($filename, true);
|
|
|
|
$this->assertEmpty($rar->error, $rar->error);
|
|
$blockList = $rar->getBlocks();
|
|
$this->assertEquals(count($blocks), count($blockList));
|
|
$this->assertEquals($blocks, $blockList);
|
|
}
|
|
|
|
/**
|
|
* Provides test data from sample files.
|
|
*/
|
|
public function providerTestFixtures()
|
|
{
|
|
$ds = DIRECTORY_SEPARATOR;
|
|
$fixturesDir = realpath(dirname(__FILE__).'/fixtures/rar');
|
|
$fixtures = array();
|
|
|
|
foreach (glob($fixturesDir.$ds.'*.rar') as $rarfile) {
|
|
$fname = pathinfo($rarfile, PATHINFO_BASENAME).'.blocks';
|
|
$fpath = $fixturesDir.$ds.$fname;
|
|
if (file_exists($fpath)) {
|
|
$blocks = include $fpath;
|
|
$fixtures[] = array('filename' => $rarfile, 'blocks' => $blocks);
|
|
}
|
|
}
|
|
|
|
return $fixtures;
|
|
}
|
|
|
|
/**
|
|
* We should be able to report on the contents of the RAR file, with some
|
|
* simple processing of the raw File blocks to make them human-readable.
|
|
*/
|
|
public function testListsAllArchiveFiles()
|
|
{
|
|
$rar = new RarInfo;
|
|
$rar->open($this->fixturesDir.'/multi.part1.rar');
|
|
|
|
$files = $rar->getFileList();
|
|
$this->assertCount(2, $files);
|
|
|
|
$this->assertSame('file1.txt', $files[0]['name']);
|
|
$this->assertSame(18, $files[0]['size']);
|
|
$this->assertSame(1258588344, $files[0]['date']);
|
|
$this->assertSame(0, $files[0]['pass']);
|
|
$this->assertSame(0, $files[0]['compressed']);
|
|
$this->assertSame('66-83', $files[0]['range']);
|
|
$this->assertSame('52b28202', $files[0]['crc32']);
|
|
$this->assertArrayNotHasKey('split', $files[0]);
|
|
$this->assertArrayNotHasKey('split_after', $files[0]);
|
|
$this->assertArrayNotHasKey('is_dir', $files[0]);
|
|
|
|
$this->assertSame('file2.txt', $files[1]['name']);
|
|
$this->assertSame(17704, $files[1]['size']);
|
|
$this->assertSame(1258588852, $files[1]['date']);
|
|
$this->assertSame(0, $files[1]['pass']);
|
|
$this->assertSame(1, $files[1]['compressed']);
|
|
$this->assertSame('130-4979', $files[1]['range']);
|
|
$this->assertSame('e0222912', $files[1]['crc32']);
|
|
$this->assertArrayHasKey('split', $files[1]);
|
|
$this->assertArrayHasKey('split_after', $files[1]);
|
|
$this->assertArrayNotHasKey('is_dir', $files[1]);
|
|
}
|
|
|
|
/**
|
|
* If the archive files are packed with the Store method, we should just be able
|
|
* to extract the file data and use it as is, since it isn't compressed.
|
|
*/
|
|
public function testExtractsFileDataPackedWithStoreMethod()
|
|
{
|
|
$rar = new RarInfo;
|
|
$rarfile = $this->fixturesDir.'/store_method.rar';
|
|
|
|
// With default byte range
|
|
$rar->open($rarfile);
|
|
$files = $rar->getFileList();
|
|
$this->assertCount(1, $files);
|
|
$this->assertSame(0, $files[0]['compressed']);
|
|
$data = $rar->getFileData($files[0]['name']);
|
|
$this->assertSame($files[0]['size'], strlen($data));
|
|
$this->assertSame($files[0]['crc32'], dechex(crc32(($data))));
|
|
$this->assertStringStartsWith('At each generation,', $data);
|
|
$this->assertStringEndsWith('children, y, is', $data);
|
|
|
|
// With range, all data available
|
|
$rar->open($rarfile, true, array(1, filesize($rarfile) - 5));
|
|
$files = $rar->getFileList();
|
|
$data = $rar->getFileData($files[0]['name']);
|
|
$this->assertSame($files[0]['size'], strlen($data));
|
|
$this->assertStringStartsWith('At each generation,', $data);
|
|
$this->assertStringEndsWith('children, y, is', $data);
|
|
|
|
// With range, partial data available
|
|
$rar->open($rarfile, true, array(1, filesize($rarfile) - 10));
|
|
$files = $rar->getFileList();
|
|
$data = $rar->getFileData($files[0]['name']);
|
|
$this->assertSame($files[0]['size'] - 2, strlen($data));
|
|
$this->assertStringStartsWith('At each generation,', $data);
|
|
$this->assertStringEndsWith('children, y, ', $data);
|
|
}
|
|
|
|
/**
|
|
* Hooray for progress! The RAR 5.0 archive format is quite different from
|
|
* earlier versions, but initially we just want to be able to detect the
|
|
* the new format automatically and not break the basic public API.
|
|
*/
|
|
public function testBasicRar50Support()
|
|
{
|
|
$rar = new RarInfo;
|
|
$rar->open($this->fixturesDir.'/rar50_encrypted_files.rar');
|
|
|
|
// New archive format should be detected
|
|
$this->assertSame(RarInfo::FMT_RAR50, $rar->format);
|
|
$this->assertEmpty($rar->error);
|
|
|
|
// File list output should be the same
|
|
$this->assertSame(4, $rar->fileCount);
|
|
$files = $rar->getFileList();
|
|
$this->assertCount(4, $files);
|
|
|
|
$this->assertSame('testdir/4mb.txt', $files[0]['name']);
|
|
$this->assertSame(4194304, $files[0]['size']);
|
|
$this->assertSame(0, $files[0]['pass']);
|
|
$this->assertSame(0, $files[0]['compressed']);
|
|
$this->assertSame(1275178921, $files[0]['date']);
|
|
$this->assertSame('122-4194425', $files[0]['range']);
|
|
$this->assertSame('def82f5', $files[0]['crc32']);
|
|
$this->assertArrayNotHasKey('is_dir', $files[0]);
|
|
|
|
$this->assertSame('testdir', $files[1]['name']);
|
|
$this->assertSame(0, $files[1]['size']);
|
|
$this->assertSame(0, $files[1]['pass']);
|
|
$this->assertSame(0, $files[1]['compressed']);
|
|
$this->assertSame(1368906855, $files[1]['date']);
|
|
$this->assertArrayNotHasKey('range', $files[1]);
|
|
$this->assertArrayNotHasKey('crc32', $files[1]);
|
|
$this->assertArrayHasKey('is_dir', $files[1]);
|
|
|
|
$this->assertSame('testdir/bar.txt', $files[2]['name']);
|
|
$this->assertSame(13, $files[2]['size']);
|
|
$this->assertSame(1, $files[2]['pass']);
|
|
$this->assertSame(1, $files[2]['compressed']);
|
|
$this->assertSame(1369170252, $files[2]['date']);
|
|
$this->assertSame('4194559-4194590', $files[2]['range']);
|
|
$this->assertSame('3b947aa0', $files[2]['crc32']);
|
|
$this->assertArrayNotHasKey('is_dir', $files[2]);
|
|
|
|
$this->assertSame('foo.txt', $files[3]['name']);
|
|
$this->assertSame(13, $files[3]['size']);
|
|
$this->assertSame(0, $files[3]['pass']);
|
|
$this->assertSame(0, $files[3]['compressed']);
|
|
$this->assertSame(1369170262, $files[3]['date']);
|
|
$this->assertSame('4194647-4194659', $files[3]['range']);
|
|
$this->assertSame('d4ac3fee', $files[3]['crc32']);
|
|
$this->assertArrayNotHasKey('is_dir', $files[3]);
|
|
|
|
$data = $rar->getFileData('foo.txt');
|
|
$this->assertSame('foo test text', $data);
|
|
$this->assertSame($files[3]['crc32'], dechex(crc32(($data))));
|
|
|
|
// Bonus! Archive comments are no longer compressed
|
|
$this->assertSame("test archive comment\x00", $rar->comments);
|
|
|
|
// Encrypted headers
|
|
$this->assertFalse($rar->isEncrypted);
|
|
$rar->open($this->fixturesDir.'/rar50_encrypted_headers.rar');
|
|
$this->assertTrue($rar->isEncrypted);
|
|
$this->assertSame(0, $rar->fileCount);
|
|
$this->assertCount(1, $rar->getBlocks());
|
|
}
|
|
|
|
/**
|
|
* We should have an easy way to retrieve a list of cached file headers from
|
|
* a RAR 5.0 Quick Open block, if it exists.
|
|
*/
|
|
public function testRar50ListsQuickOpenCachedFiles()
|
|
{
|
|
$rar = new RarInfo;
|
|
$rar->open($this->fixturesDir.'/rar50_quickopen.rar');
|
|
$this->assertSame(RarInfo::FMT_RAR50, $rar->format);
|
|
$this->assertEmpty($rar->error);
|
|
|
|
$files = $rar->getQuickOpenFileList();
|
|
$this->assertCount(4, $files);
|
|
|
|
$this->assertSame('testdir/4mb.txt', $files[0]['name']);
|
|
$this->assertSame(4194304, $files[0]['size']);
|
|
$this->assertSame(0, $files[0]['compressed']);
|
|
$this->assertArrayNotHasKey('range', $files[0]);
|
|
|
|
$this->assertSame('testdir', $files[1]['name']);
|
|
$this->assertArrayHasKey('is_dir', $files[1]);
|
|
|
|
$this->assertSame('compressed.txt', $files[2]['name']);
|
|
$this->assertSame(4194304, $files[2]['size']);
|
|
$this->assertSame(1, $files[2]['compressed']);
|
|
$this->assertArrayNotHasKey('range', $files[2]);
|
|
|
|
$this->assertSame('bar.txt', $files[3]['name']);
|
|
$this->assertSame(13, $files[3]['size']);
|
|
$this->assertSame(1, $files[3]['pass']);
|
|
$this->assertSame(1, $files[3]['compressed']);
|
|
$this->assertArrayNotHasKey('range', $files[3]);
|
|
}
|
|
|
|
/**
|
|
* We want to bail as early as possible when handling archives with encrypted
|
|
* headers, as there's not much else we can do with them.
|
|
*/
|
|
public function testHandlesEncryptedArchivesGracefully()
|
|
{
|
|
$rar = new RarInfo;
|
|
$rar->open($this->fixturesDir.'/encrypted_headers.rar');
|
|
$this->assertTrue($rar->isEncrypted);
|
|
$this->assertCount(2, $rar->getBlocks());
|
|
|
|
$summary = $rar->getSummary(true);
|
|
$this->assertSame(1, $summary['is_encrypted']);
|
|
$this->assertSame(0, $summary['file_count']);
|
|
$this->assertSame(0, $rar->fileCount);
|
|
$this->assertEmpty($summary['file_list']);
|
|
$this->assertCount(0, $rar->getFileList());
|
|
}
|
|
|
|
/**
|
|
* Provides the path to the external client executable, or false if it
|
|
* doesn't exist in the given directory.
|
|
*
|
|
* @return string|boolean the absolute path to the executable, or false
|
|
*/
|
|
protected function getUnrarPath()
|
|
{
|
|
$unrar = DIRECTORY_SEPARATOR === '\\'
|
|
? dirname(__FILE__).'\bin\unrar\UnRAR.exe'
|
|
: dirname(__FILE__).'/bin/unrar/unrar';
|
|
|
|
if (file_exists($unrar))
|
|
return $unrar;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Decompression of archive contents should be possible by using an external
|
|
* client to read the current file, or temporary files for data sources. The
|
|
* test should be skipped if no external client is available.
|
|
*
|
|
* @group external
|
|
*/
|
|
public function testDecompressesWithExternalClient()
|
|
{
|
|
if (!($unrar = $this->getUnrarPath())) {
|
|
$this->markTestSkipped();
|
|
}
|
|
$rar = new RarInfo;
|
|
|
|
// From a file source
|
|
$rarfile = $this->fixturesDir.'/solid.rar';
|
|
$rar->open($rarfile);
|
|
$this->assertEmpty($rar->error);
|
|
|
|
$files = $rar->getFileList();
|
|
$file = $files[1];
|
|
$this->assertSame('unrardll.txt', $file['name']);
|
|
$this->assertSame(1, $file['compressed']);
|
|
|
|
$data = $rar->extractFile($file['name']);
|
|
$this->assertNotEmpty($rar->error);
|
|
$this->assertContains('external client', $rar->error);
|
|
$this->assertFalse($data);
|
|
|
|
$rar->setExternalClient($unrar);
|
|
$data = $rar->extractFile($file['name']);
|
|
$this->assertEmpty($rar->error);
|
|
$this->assertSame($file['size'], strlen($data));
|
|
$this->assertSame($file['crc32'], dechex(crc32(($data))));
|
|
$this->assertStringStartsWith("\r\n UnRAR.dll Manual", $data);
|
|
$this->assertStringEndsWith("to access this function.\r\n\r\n", $data);
|
|
|
|
// From a data source (via temp file)
|
|
$rar->setData(file_get_contents($rarfile));
|
|
$this->assertEmpty($rar->error);
|
|
$summary = $rar->getSummary(true);
|
|
$this->assertSame(filesize($rarfile), $summary['data_size']);
|
|
$data = $rar->extractFile($file['name']);
|
|
$this->assertEmpty($rar->error);
|
|
$this->assertSame($file['size'], strlen($data));
|
|
}
|
|
|
|
/**
|
|
* Fixes issue #9, memory exhaustion on bad RAR50 file
|
|
*
|
|
* @depends testBasicRar50Support
|
|
*/
|
|
public function testRar50BadExtraSize()
|
|
{
|
|
$rar = new RarInfo;
|
|
$rar->open($this->fixturesDir.'/rar50_bad_extra_size.rar');
|
|
|
|
$this->assertSame(RarInfo::FMT_RAR50, $rar->format);
|
|
$this->assertEmpty($rar->error);
|
|
|
|
// The third file entry is corrupt, so isn't displayed in file list
|
|
$this->assertSame(3, $rar->fileCount);
|
|
$files = $rar->getFileList();
|
|
$this->assertCount(2, $files);
|
|
|
|
// Bad block, header size points beyond file end
|
|
$blocks = $rar->getBlocks();
|
|
$this->assertSame(5, count($blocks));
|
|
$this->assertSame('File', $blocks[4]['type']);
|
|
$this->assertSame(11, $blocks[4]['extra_size']);
|
|
$this->assertEmpty($blocks[4]['file_name']);
|
|
|
|
// Comments in summary
|
|
$summary = $rar->getSummary();
|
|
$this->assertArrayHasKey('comments', $summary);
|
|
$this->assertStringStartsWith('German:', $summary['comments']);
|
|
}
|
|
|
|
} // End RarInfoTest
|
|
|