fixturesDir = realpath(dirname(__FILE__).'/fixtures/szip'); } /** * 7z files consist of headers at the archive start and end, with the blocks * of packed data streams between. We should be abe to report an accurate list * of all headers in summmary form. * * @dataProvider providerTestFixtures * @param string $filename sample 7z filename * @param string $headers expected list of valid headers */ public function testStoresListOfAllValidHeaders($filename, $headers) { $szip = new SzipInfo; $szip->open($filename, true); $this->assertEmpty($szip->error, $szip->error); $headerList = $szip->getHeaders(true); $this->assertEquals(count($headers), count($headerList)); $this->assertEquals($headers, $headerList); } /** * Provides test data from sample files. */ public function providerTestFixtures() { $ds = DIRECTORY_SEPARATOR; $fixturesDir = realpath(dirname(__FILE__).'/fixtures/szip'); $fixtures = array(); foreach (glob($fixturesDir.$ds.'*.{7z,001,002}', GLOB_BRACE) as $szipfile) { $fname = pathinfo($szipfile, PATHINFO_BASENAME).'.headers'; $fpath = $fixturesDir.$ds.$fname; if (file_exists($fpath)) { $headers = include $fpath; $fixtures[] = array('filename' => $szipfile, 'headers' => $headers); } } return $fixtures; } /** * We should be able to report on the contents of the 7z archive, with some * simple processing of the raw headers to make them human-readable. It's * especially helpful to know if we're dealing with files stored in solid * archives and/or compressed substreams. */ public function testListsAllArchiveFiles() { $szip = new SzipInfo; // Without substreams $szip->open($this->fixturesDir.'/store_method.7z'); $this->assertEmpty($szip->error); $this->assertFalse($szip->isSolid); $this->assertSame(2, $szip->blockCount); $files = $szip->getFileList(); $this->assertCount(2, $files); $file = $files[0]; $this->assertSame('7zFormat.txt', $file['name']); $this->assertSame(7573, $file['size']); $this->assertSame(1284641836, $file['date']); $this->assertSame(0, $file['pass']); $this->assertSame(0, $file['compressed']); $this->assertSame(0, $file['block']); $this->assertSame('32-7604', $file['range']); $this->assertSame('3f8ccf66', $file['crc32']); $this->assertArrayNotHasKey('is_dir', $file); $file = $files[1]; $this->assertSame('foo.txt', $file['name']); $this->assertSame(15, $file['size']); $this->assertSame(1373228890, $file['date']); $this->assertSame(0, $file['pass']); $this->assertSame(0, $file['compressed']); $this->assertSame(1, $file['block']); $this->assertSame('7605-7619', $file['range']); $this->assertSame('1a92d0b1', $file['crc32']); $this->assertArrayNotHasKey('is_dir', $file); // With compressed substreams $szip->open($this->fixturesDir.'/solid_lzma_multi.7z'); $this->assertEmpty($szip->error); $this->assertTrue($szip->isSolid); $this->assertSame(1, $szip->blockCount); $files = $szip->getFileList(); $this->assertCount(3, $files); $file = $files[0]; $this->assertSame('7zFormat.txt', $file['name']); $this->assertSame(7573, $file['size']); $this->assertSame(1, $file['compressed']); $this->assertSame(0, $file['block']); $this->assertSame('32-2087', $file['range']); $this->assertSame('3f8ccf66', $file['crc32']); $file = $files[1]; $this->assertSame('bar.txt', $file['name']); $this->assertSame(15, $file['size']); $this->assertSame(1, $file['compressed']); $this->assertSame(0, $file['block']); $this->assertSame('32-2087', $file['range']); $this->assertSame('71afb453', $file['crc32']); $file = $files[2]; $this->assertSame('foo.txt', $file['name']); $this->assertSame(15, $file['size']); $this->assertSame(1, $file['compressed']); $this->assertSame(0, $file['block']); $this->assertSame('32-2087', $file['range']); $this->assertSame('1a92d0b1', $file['crc32']); // With directories/empty streams $szip->open($this->fixturesDir.'/store_with_empty.7z'); $this->assertEmpty($szip->error); $this->assertSame(2, $szip->blockCount); $files = $szip->getFileList(); $this->assertCount(4, $files); $file = $files[0]; $this->assertSame('testdir3/7zFormat.txt', $file['name']); $this->assertSame(7573, $file['size']); $this->assertSame(0, $file['block']); $file = $files[1]; $this->assertSame('testdir3/foo.txt', $file['name']); $this->assertSame(15, $file['size']); $this->assertSame(1, $file['block']); $file = $files[2]; $this->assertSame('testdir3/empty.txt', $file['name']); $this->assertSame(0, $file['size']); $this->assertArrayNotHasKey('block', $file); $this->assertArrayNotHasKey('range', $file); $this->assertArrayNotHasKey('crc32', $file); $this->assertArrayNotHasKey('is_dir', $file); $file = $files[3]; $this->assertSame('testdir3', $file['name']); $this->assertSame(0, $file['size']); $this->assertSame(1, $file['is_dir']); $this->assertArrayNotHasKey('block', $file); $this->assertArrayNotHasKey('crc32', $file); $this->assertArrayNotHasKey('range', $file); } /** * File ranges should reflect the file data actually available in the current * source, even if only partial and/or the Start header is missing or skipped. * If no file data is available, ranges should not be included in the file info. */ public function testListsPackedRangesWithPartialData() { $szip = new SzipInfo; // Without start header (multi-volume) $file = $this->fixturesDir.'/multi_volume.7z.002'; $end = filesize($file) - 1; $szip->open($file, true, array(2, $end)); $files = $szip->getFileList(); $this->assertSame('2-3508', $files[0]['range']); $this->assertSame('3509-3523', $files[1]['range']); $szip->open($file, true, array(3508, $end)); $files = $szip->getFileList(); $this->assertSame('3508-3508', $files[0]['range']); $this->assertSame('3509-3523', $files[1]['range']); $szip->open($file, true, array(3510, $end)); $files = $szip->getFileList(); $this->assertArrayNotHasKey('range', $files[0]); $this->assertSame('3510-3523', $files[1]['range']); // With start header (skipped) $file = $this->fixturesDir.'/store_with_directories.7z';; $end = filesize($file) - 1; $szip->open($file, true, array(2, $end)); $files = $szip->getFileList(); $this->assertSame('32-7604', $files[0]['range']); $this->assertSame('7605-7619', $files[1]['range']); $szip->open($file, true, array(7604, $end)); $files = $szip->getFileList(); $this->assertSame('7604-7604', $files[0]['range']); $this->assertSame('7605-7619', $files[1]['range']); $this->assertSame('7635-7649', $files[3]['range']); $szip->open($file, true, array(7605, $end)); $files = $szip->getFileList(); $this->assertArrayNotHasKey('range', $files[0]); $this->assertSame('7605-7619', $files[1]['range']); $this->assertSame('7635-7649', $files[3]['range']); $szip->open($file, true, array(7640, $end)); $files = $szip->getFileList(); $this->assertArrayNotHasKey('range', $files[0]); $this->assertArrayNotHasKey('range', $files[1]); $this->assertArrayNotHasKey('range', $files[2]); $this->assertSame('7640-7649', $files[3]['range']); } /** * If the archive files aren't compressed, we should just be able to extract * the file data and use it as is. * * @depends testListsPackedRangesWithPartialData */ public function testExtractsUncompressedFileData() { $szip = new SzipInfo; // With all data available $szip->open($this->fixturesDir.'/store_method.7z'); $this->assertEmpty($szip->error); $files = $szip->getFileList(); $this->assertCount(2, $files); $this->assertSame('7zFormat.txt', $files[0]['name']); $this->assertSame(0, $files[0]['compressed']); $data = $szip->getFileData($files[0]['name']); $this->assertSame($files[0]['size'], strlen($data)); $this->assertSame($files[0]['crc32'], dechex(crc32($data))); $this->assertStringStartsWith('7z Format description', $data); $this->assertStringEndsWith("End of document\r\n", $data); $this->assertSame('foo.txt', $files[1]['name']); $this->assertSame(0, $files[1]['compressed']); $data = $szip->getFileData($files[1]['name']); $this->assertSame($files[1]['size'], strlen($data)); $this->assertSame($files[1]['crc32'], dechex(crc32($data))); $this->assertSame('sample foo text', $data); // With partial data available $szip->open($this->fixturesDir.'/multi_volume.7z.002', true); $this->assertEmpty($szip->error); $files = $szip->getFileList(); $this->assertCount(2, $files); $this->assertSame('7zFormat.txt', $files[0]['name']); $this->assertSame(0, $files[0]['compressed']); $data = $szip->getFileData($files[0]['name']); $this->assertNotSame($files[0]['size'], strlen($data)); $this->assertStringStartsWith('odecIdSize', $data); $this->assertStringEndsWith("End of document\r\n", $data); } /** * 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 getUnzipPath() { $unzip = DIRECTORY_SEPARATOR === '\\' ? dirname(__FILE__).'\bin\7z\7za.exe' : dirname(__FILE__).'/bin/7z/7za'; if (file_exists($unzip)) return $unzip; return false; } /** * Decompression and/or decryption 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 testExtractsFilesWithExternalClient() { if (!($unzip = $this->getUnzipPath())) { $this->markTestSkipped(); } $szip = new SzipInfo; // Compressed files $szfile = $this->fixturesDir.'/solid_lzma_multi.7z'; $szip->open($szfile); $this->assertEmpty($szip->error); $files = $szip->getFileList(); $file = $files[0]; $this->assertSame('7zFormat.txt', $file['name']); $this->assertSame(1, $file['compressed']); $data = $szip->extractFile($file['name']); $this->assertNotEmpty($szip->error); $this->assertContains('external client', $szip->error); $szip->setExternalClient($unzip); $data = $szip->extractFile($file['name']); $this->assertEmpty($szip->error); $this->assertSame(strlen($data), $file['size']); $this->assertSame($file['crc32'], dechex(crc32($data))); $this->assertStringStartsWith('7z Format description', $data); $this->assertStringEndsWith("End of document\r\n", $data); // Encrypted files $szfile = $this->fixturesDir.'/multi_substreams_encrypted.7z'; $szip->open($szfile); $this->assertEmpty($szip->error); $files = $szip->getFileList(); $file = $files[2]; $this->assertSame('7zFormat.txt', $file['name']); $this->assertSame(1, $file['pass']); $data = $szip->extractFile($file['name']); $this->assertNotEmpty($szip->error); $this->assertContains('passworded', $szip->error); $data = $szip->extractFile($file['name'], null, 'password'); $this->assertEmpty($szip->error, $szip->error); $this->assertSame(strlen($data), $file['size']); $this->assertSame($file['crc32'], dechex(crc32($data))); $this->assertStringStartsWith('7z Format description', $data); $this->assertStringEndsWith("End of document\r\n", $data); // From a data source (via temp file) $szip->setData(file_get_contents($szfile)); $this->assertEmpty($szip->error); $summary = $szip->getSummary(true); $this->assertSame(filesize($szfile), $summary['data_size']); $data = $szip->extractFile($file['name'], null, 'password'); $this->assertEmpty($szip->error); $this->assertSame($file['size'], strlen($data)); $this->assertSame($file['crc32'], dechex(crc32($data))); } /** * Headers can be compressed or encrypted, and we should be able to use an * external client to extract these before further parsing. * * @depends testExtractsFilesWithExternalClient * @group external */ public function testExtractsEncodedHeadersWithExternalClient() { if (!($unzip = $this->getUnzipPath())) { $this->markTestSkipped(); } $szip = new SzipInfo; // Compressed headers $file = $this->fixturesDir.'/store_method_enc_headers.7z'; $szip->open($file); $this->assertEmpty($szip->error); $this->assertSame(1, $szip->blockCount); $this->assertEmpty($szip->getFileList()); $szip->setExternalClient($unzip); $szip->open($file); $this->assertEmpty($szip->error); $this->assertSame(2, $szip->blockCount); $files = $szip->getFileList(); $this->assertCount(2, $files); $file = $files[0]; $this->assertSame('7zFormat.txt', $file['name']); $this->assertSame('32-7604', $file['range']); $file = $files[1]; $this->assertSame('foo.txt', $file['name']); $this->assertSame('7605-7619', $file['range']); // Encrypted headers $file = $this->fixturesDir.'/encrypted_headers.7z'; $szip->open($file); $this->assertNotEmpty($szip->error); $this->assertContains('password needed', $szip->error); $this->assertTrue($szip->isEncrypted); $this->assertSame(1, $szip->blockCount); $this->assertEmpty($szip->getFileList()); $szip->setPassword('password'); $szip->open($file); $this->assertEmpty($szip->error); $this->assertSame(1, $szip->blockCount); $files = $szip->getFilelist(); $this->assertCount(1, $files); $file = $files[0]; $this->assertSame('7zFormat.txt', $file['name']); $this->assertSame(7573, $file['size']); $this->assertSame('32-7615', $file['range']); $this->assertSame(1, $file['pass']); $this->assertSame(0, $file['compressed']); } } // End SzipInfoTest