21 Commits
Author SHA1 Message Date
itismadness fe026fb3a3 Fix path to create BencodeTorrent 2023-10-19 05:25:23 +00:00
itismadness 922a60224d Update README usage info 2023-10-19 05:20:30 +00:00
itismadness a56d7d85c2 Update test README badge 2023-10-13 00:12:17 +00:00
itismadness d2dacb03ec Add empty data validate test 2023-10-13 00:09:21 +00:00
itismadness 9a030b6823 Add phpunit cache to gitignore 2023-10-13 00:01:20 +00:00
itismadness 2addd8d588 Update minimum PHP to 8.1 2023-10-13 00:00:15 +00:00
itismadness ea558fc679 Replace deprecated utf8_encode function 2023-10-12 23:59:53 +00:00
itismadness 42f5170482 Mark as php 8.0 compatible 2021-08-22 14:43:26 +00:00
itismadness 917cf865a9 allow empty components on file path so long as not totally empty 2020-10-12 00:27:44 +00:00
itismadness ca9617422a add getCreationDate method 2020-10-03 20:31:03 +00:00
itismadness 5ce1e6c6cc remove unnecessary devDependency 2020-10-03 19:17:16 +00:00
itismadness 6778e228fb update README 2020-10-03 19:16:39 +00:00
itismadness 9748eef34d move to github actions for testing 2020-10-03 19:07:52 +00:00
itismadness 3efb191299 add phpstan for testing 2020-10-03 19:01:33 +00:00
itismadness bdbc17e173 enable strict types 2020-10-03 19:01:22 +00:00
itismadness a9229a067b move to PSR12 coding standard 2020-10-03 18:47:30 +00:00
itismadness 93707559cc update to phpunit 8 2020-10-03 18:42:13 +00:00
itismadness 1b414569bd add getSource function 2020-10-03 18:35:07 +00:00
itismadness e07f029d12 Add back in PHP 7.2 to test Travis 2018-11-10 12:30:07 -05:00
itismadness 95374ae069 Test TravisCI not building commits 2018-11-10 12:19:25 -05:00
it is madness 11c2e4519c Update README.md 2018-11-10 12:15:38 -05:00
12 changed files with 329 additions and 218 deletions

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
name: Test
on:
push:
branches: [ '*' ]
pull_request:
branches: [ '*' ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['8.1', '8.2']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
tools: composer, pecl
coverage: pcov
- name: Validate composer.json and composer.lock
run: composer validate
- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v2
with:
path: vendor
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.php-version }}-composer-
${{ runner.os }}-composer-
- name: Install dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
run: composer install --prefer-dist --no-progress --no-suggest
- run: composer lint
- run: composer stan
- run: composer test
+2
View File
@@ -3,3 +3,5 @@ report/
vendor/
.DS_Store
composer.lock
.phpunit.result.cache
.phpunit.cache
-27
View File
@@ -1,27 +0,0 @@
language: php
sudo: true
dist: trusty
php:
- '7.0'
- '7.1'
- '7.2'
- '7.3'
- nightly
matrix:
fast_finish: true
allow_failures:
- php: nightly
cache:
directories:
- $HOME/.composer/cache
install:
- composer install
script:
- vendor/bin/phpunit
- vendor/bin/phpcs src
- vendor/bin/phpcs tests
+16 -7
View File
@@ -1,33 +1,41 @@
BEncode Torrent
===============
[[![Build Status](https://travis-ci.org/OPSnet/bencode-torrent.svg?branch=master)](https://travis-ci.org/OPSnet/bencode-torrent)
[![Test](https://github.com/OPSnet/bencode-torrent/actions/workflows/test.yml/badge.svg?branch=master&event=push)](https://github.com/OPSnet/bencode-torrent/actions/workflows/test.yml)
[![Packagist](https://img.shields.io/packagist/v/orpheusnet/bencode-torrent.svg)](https://packagist.org/packages/orpheusnet/bencode-torrent)
![PHP from Travis config](https://img.shields.io/travis/php-v/OPSnet/bencode-torrent.svg)
![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/orpheusnet/bencode-torrent)
PHP library for encoding and decoding BitTorrent BEncode data, focused around usage within
[Gazelle](https://github.com/OPSnet/Gazelle).
Usage
-----
`composer require orpheusnet/bencode-torrent`
```php
use OrpheusNET\BencodeTorrent;
$bencode = new Bencode();
require('vendor/autoload.php');
$bencode = new \OrpheusNET\BencodeTorrent\BencodeTorrent();
$bencode->decodeFile('path/to/file.torrent');
var_dump($bencode->getName());
var_dump($bencode->getHexInfoHash());
var_dump($bencode->getFileList());
```
See the [`BencodeTorrent`](src/BencodeTorrent.php) class for more information on the available methods.
Additionally, see [`Bencode`](src/Bencode.php) for the underlying library to encode/decode BEncode data.
Description
-----------
BEncode is the encoding used by BitTorrent to store and transmitting loosely structured data. It supports
BEncode is the encoding used by BitTorrent to store and transmitting loosely structured data. It supports:
* byte strings
* integers
* lists
* dictionaries (associative arrays, where keys are sorted alphabetically)
You can see more information about how these types are supported at
You can see more information about how these types are supported at
[BitTorrentSpecification#Bencoding](https://wiki.theory.org/index.php/BitTorrentSpecification#Bencoding).
In addition to the above, torrent files are expected to be BEncoded dictionaries that contain minimally the keys
@@ -42,6 +50,7 @@ raised. More information on these fields can be found at
Finally, this library is primarily aimed at being used within the [Gazelle](https://github.com/OPSnet/Gazelle) so
we have some utility functions within the library that make sense there to accomplish the following things:
* Ensuring torrent files are marked as 'private'
* Setting a 'source' on torrents (to ensure unique info hash)
* Cleaning out unnecessary fields that also reveal stuff about a user (like __announce list__ and __created by__)
@@ -50,4 +59,4 @@ we have some utility functions within the library that make sense there to accom
This is based (loosely) off the code in the two separate BEncode libraries within WCD's Gazelle
([bencodetorrent.class.php](https://github.com/WhatCD/Gazelle/blob/master/classes/bencodetorrent.class.php) and
[torrent.class.php](https://github.com/WhatCD/Gazelle/blob/master/classes/torrent.class.php)), but without the now
unnecessary 32bit shims as well as making it a unified library used for both uploading and downloading the torrent files.
unnecessary 32bit shims as well as making it a unified library used for both uploading and downloading the torrent files.
+11 -4
View File
@@ -15,11 +15,18 @@
}
},
"require": {
"php": "^7"
"php": ">=8.1",
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^6|^7",
"squizlabs/php_codesniffer": "3.*",
"ext-mbstring": "^7"
"phpunit/phpunit": "^10.4.1",
"squizlabs/php_codesniffer": "^3.7.2",
"phpstan/phpstan": "^1.10.38"
},
"scripts": {
"test": "phpunit",
"lint": "phpcs",
"lint:fix": "phpcbf",
"stan": "phpstan analyse"
}
}
+5 -36
View File
@@ -1,38 +1,7 @@
<?xml version="1.0"?>
<ruleset name="Gazelle Standard">
<description>The coding standard for the Gazelle project</description>
<ruleset name="PSR12 Standard">
<file>src</file>
<file>tests</file>
<!-- Include the whole PSR-1 standard -->
<rule ref="PSR1"/>
<!-- Include the whole PSR-2 standard -->
<rule ref="PSR2">
<exclude name="PSR2.Classes.ClassDeclaration.OpenBraceNewLine"/>
</rule>
<!-- Do not allow trailing whitespace at the end of lines -->
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace" />
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration.BraceOnSameLine">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ControlSignature.SpaceAfterCloseBrace">
<severity>0</severity>
</rule>
<rule ref="Generic.Classes.OpeningBraceSameLine"/>
<rule ref="Generic.Functions.OpeningFunctionBraceKernighanRitchie"/>
<!-- Ensure that proper line endings are used -->
<rule ref="Generic.Files.LineEndings">
<properties>
<property name="eolChar" value="\n"/>
</properties>
</rule>
<!-- Increase PHPCS/PEAR limit of 85 chars per line to 100 -->
<rule ref="Generic.Files.LineLength">
<properties>
<property name="lineLimit" value="100"/>
<property name="absoluteLineLimit" value="0"/>
</properties>
</rule>
</ruleset>
<rule ref="PSR12" />
</ruleset>
+5
View File
@@ -0,0 +1,5 @@
parameters:
level: 5
paths:
- src
- tests
+20 -19
View File
@@ -1,19 +1,20 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.5/phpunit.xsd"
colors="true"
bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="unit">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
<exclude />
</whitelist>
</filter>
<logging>
<log type="coverage-html" target="report" />
</logging>
</phpunit>
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.4/phpunit.xsd" colors="true" bootstrap="vendor/autoload.php" cacheDirectory=".phpunit.cache">
<coverage>
<report>
<html outputDirectory="report"/>
</report>
</coverage>
<testsuites>
<testsuite name="unit">
<directory>tests</directory>
</testsuite>
</testsuites>
<logging/>
<source>
<include>
<directory suffix=".php">src</directory>
</include>
<exclude/>
</source>
</phpunit>
+32 -27
View File
@@ -1,16 +1,20 @@
<?php
declare(strict_types=1);
namespace OrpheusNET\BencodeTorrent;
class Bencode {
protected $data = null;
class Bencode
{
/** @var array */
protected $data;
/**
* Sets the internal data array
* @param mixed $data
* @throws \RuntimeException
*/
public function setData($data) {
public function setData($data)
{
$this->data = $data;
}
@@ -19,7 +23,8 @@ class Bencode {
* @param string $data
* @throws \RuntimeException
*/
public function decodeString(string $data) {
public function decodeString(string $data)
{
$this->data = $this->decode($data);
}
@@ -29,8 +34,9 @@ class Bencode {
* @param string $path
* @throws \RuntimeException
*/
public function decodeFile(string $path) {
$this->data = $this->decode(file_get_contents($path, FILE_BINARY));
public function decodeFile(string $path)
{
$this->data = $this->decode(file_get_contents($path));
}
/**
@@ -46,7 +52,8 @@ class Bencode {
* @param int $pos
* @return mixed
*/
protected function decode(string $data, int &$pos = 0) {
protected function decode(string $data, int &$pos = 0)
{
$start_decode = $pos === 0;
if ($data[$pos] === 'd') {
$pos++;
@@ -58,14 +65,13 @@ class Bencode {
break;
}
if (!is_string($key)) {
throw new \RuntimeException('Invalid key type, must be string: '.gettype($key));
throw new \RuntimeException('Invalid key type, must be string: ' . gettype($key));
}
$return[$key] = $value;
}
ksort($return);
$pos++;
}
elseif ($data[$pos] === 'l') {
} elseif ($data[$pos] === 'l') {
$pos++;
$return = [];
while ($data[$pos] !== 'e') {
@@ -73,8 +79,7 @@ class Bencode {
$return[] = $value;
}
$pos++;
}
elseif ($data[$pos] === 'i') {
} elseif ($data[$pos] === 'i') {
$pos++;
$digits = strpos($data, 'e', $pos) - $pos;
$return = substr($data, $pos, $digits);
@@ -87,13 +92,12 @@ class Bencode {
$return = substr($return, 1);
}
if (!ctype_digit($return)) {
$msg = 'Cannot have non-digit values in integer number: '.$return;
$msg = 'Cannot have non-digit values in integer number: ' . $return;
throw new \RuntimeException($msg);
}
$return = $multiplier * ((int) $return);
$pos += $digits + 1;
}
else {
} else {
$digits = strpos($data, ':', $pos) - $pos;
$len = (int) substr($data, $pos, $digits);
$pos += ($digits + 1);
@@ -112,14 +116,16 @@ class Bencode {
* Get the internal data array
* @return mixed
*/
public function getData() {
public function getData()
{
return $this->data;
}
/**
* @throws \RuntimeException
*/
protected function hasData() {
protected function hasData()
{
if ($this->data === null) {
throw new \RuntimeException('Must decode proper bencode string first');
}
@@ -128,7 +134,8 @@ class Bencode {
/**
* @return string
*/
public function getEncode() : string {
public function getEncode(): string
{
$this->hasData();
return $this->encodeVal($this->data);
}
@@ -137,7 +144,8 @@ class Bencode {
* @param mixed $data
* @return string
*/
protected function encodeVal($data) : string {
protected function encodeVal($data): string
{
if (is_array($data)) {
$return = '';
$check = -1;
@@ -154,8 +162,7 @@ class Bencode {
foreach ($data as $value) {
$return .= $this->encodeVal($value);
}
}
else {
} else {
$return .= 'd';
foreach ($data as $key => $value) {
$return .= $this->encodeVal(strval($key));
@@ -163,11 +170,9 @@ class Bencode {
}
}
$return .= 'e';
}
elseif (is_integer($data)) {
$return = 'i'.$data.'e';
}
else {
} elseif (is_integer($data)) {
$return = 'i' . $data . 'e';
} else {
$return = strlen($data) . ':' . $data;
}
return $return;
+77 -45
View File
@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
namespace OrpheusNET\BencodeTorrent;
/**
@@ -24,11 +26,14 @@ namespace OrpheusNET\BencodeTorrent;
* For Gazelle, this also acts as a unification of the two original BEncode implementations
* which were both used in separate areas of the codebase.
*/
class BencodeTorrent extends Bencode {
const FILELIST_DELIM = 0xF7;
class BencodeTorrent extends Bencode
{
public const FILELIST_DELIM = 0xF7;
/** @var string */
private static $utf8_filelist_delim = null;
public function __construct() {
public function __construct()
{
$this->setDelim();
}
@@ -37,9 +42,10 @@ class BencodeTorrent extends Bencode {
* and char to set a class constant or variable, so we wait till the class is initialized
* for the first time to set it.
*/
private function setDelim() {
private function setDelim(): void
{
if (self::$utf8_filelist_delim === null) {
self::$utf8_filelist_delim = utf8_encode(chr(self::FILELIST_DELIM));
self::$utf8_filelist_delim = mb_convert_encoding(chr(self::FILELIST_DELIM), 'UTF-8', 'ISO-8859-1');
}
}
@@ -48,7 +54,8 @@ class BencodeTorrent extends Bencode {
* @param array $data
* @throws \RuntimeException
*/
public function setData($data) {
public function setData($data): void
{
parent::setData($data);
$this->validate();
}
@@ -58,7 +65,8 @@ class BencodeTorrent extends Bencode {
* @param string $data
* @throws \RuntimeException
*/
public function decodeString(string $data) {
public function decodeString(string $data): void
{
parent::decodeString($data);
$this->validate();
}
@@ -69,16 +77,18 @@ class BencodeTorrent extends Bencode {
* @param string $path
* @throws \RuntimeException
*/
public function decodeFile(string $path) {
public function decodeFile(string $path): void
{
parent::decodeFile($path);
$this->validate();
}
/**
* Validates that the internal data array
* @throws \RuntimeException
* @throws \TypeError|\RuntimeException
*/
public function validate() {
public function validate(): void
{
if (!is_array($this->data)) {
throw new \TypeError('Data must be an array');
}
@@ -95,8 +105,8 @@ class BencodeTorrent extends Bencode {
return strlen($element) === 0;
}
);
if (count($filter) > 0) {
throw new \RuntimeException('Cannot have empty path for a file');
if (count($filter) === count($file[$path_key]) && $file['length'] !== 0) {
throw new \RuntimeException('Cannot have a file with no path');
}
}
}
@@ -111,7 +121,8 @@ class BencodeTorrent extends Bencode {
*
* @return bool flag to indicate if we altered the info dictionary
*/
public function clean() : bool {
public function clean(): bool
{
$this->cleanDataDictionary();
return $this->cleanInfoDictionary();
}
@@ -121,7 +132,8 @@ class BencodeTorrent extends Bencode {
* overwritten dynamically on any downloaded torrent (like announce or comment), so that we
* store the smallest encoded string within the database and cuts down on potential waste.
*/
public function cleanDataDictionary() {
public function cleanDataDictionary(): void
{
$allowed_keys = array('encoding', 'info');
foreach ($this->data as $key => $value) {
if (!in_array($key, $allowed_keys)) {
@@ -144,7 +156,8 @@ class BencodeTorrent extends Bencode {
*
* @return bool
*/
public function cleanInfoDictionary() : bool {
public function cleanInfoDictionary(): bool
{
$cleaned = false;
$allowed_keys = array('files', 'name', 'piece length', 'pieces', 'private', 'length',
'name.utf8', 'name.utf-8', 'md5sum', 'sha1', 'source',
@@ -164,7 +177,8 @@ class BencodeTorrent extends Bencode {
*
* @return bool
*/
public function isPrivate() : bool {
public function isPrivate(): bool
{
$this->hasData();
return isset($this->data['info']['private']) && $this->data['info']['private'] === 1;
}
@@ -178,7 +192,8 @@ class BencodeTorrent extends Bencode {
*
* @return bool
*/
public function makePrivate() : bool {
public function makePrivate(): bool
{
$this->hasData();
if ($this->isPrivate()) {
return false;
@@ -194,12 +209,9 @@ class BencodeTorrent extends Bencode {
* 'official' flag (no accepted BEP on it), but it has become the defacto standard with more
* clients supporting it natively. Returns a boolean on whether or not the source was changed
* so that an appropriate screen can be shown to the user.
*
* @param string $source
*
* @return bool true if the source was set/changed, false if no change
*/
public function setSource(string $source) : bool {
public function setSource(string $source): bool
{
$this->hasData();
if (isset($this->data['info']['source']) && $this->data['info']['source'] === $source) {
return false;
@@ -212,15 +224,32 @@ class BencodeTorrent extends Bencode {
return true;
}
/**
* Get the source flag if one has been set
*/
public function getSource(): ?string
{
$this->hasData();
return $this->data['info']['source'] ?? null;
}
/**
* Get the creation date for torrent if one has been set
*/
public function getCreationDate(): ?int
{
$this->hasData();
return $this->data['creation date'] ?? null;
}
/**
* Function to allow you set any number of keys and values in the data dictionary. You can
* set the value in a dictionary by concatenating the keys into a string with a period
* separator (ex: info.name will set name field in the info dictionary) so that the rest
* of the dictionary is unaffected.
*
* @param array $array
*/
public function setValue(array $array) {
public function setValue(array $array): void
{
foreach ($array as $key => $value) {
if (is_array($value)) {
ksort($value);
@@ -253,19 +282,20 @@ class BencodeTorrent extends Bencode {
*
* @return string
*/
public function getInfoHash() : string {
public function getInfoHash(): string
{
$this->hasData();
return sha1($this->encodeVal($this->data['info']));
}
public function getHexInfoHash(): string {
public function getHexInfoHash(): string
{
return pack('H*', $this->getInfoHash());
}
/**
* @return string
*/
public function getName() {
public function getName(): string
{
$this->hasData();
if (isset($this->data['info']['name.utf-8'])) {
return $this->data['info']['name.utf-8'];
}
@@ -276,15 +306,14 @@ class BencodeTorrent extends Bencode {
* Get the total size in bytes of the files in the torrent. For a single file torrent, it'll
* just be the 'length' key in the 'info' dictionary, else we iterate through the 'files' list
* and add up the 'length' of each element.
*
* @return int
*/
public function getSize() : int {
public function getSize(): int
{
$this->hasData();
$cur_size = 0;
if (!isset($this->data['info']['files'])) {
$cur_size = $this->data['info']['length'];
}
else {
} else {
foreach ($this->data['info']['files'] as $file) {
$cur_size += $file['length'];
}
@@ -301,7 +330,8 @@ class BencodeTorrent extends Bencode {
*
* @return array
*/
public function getFileList() : array {
public function getFileList(): array
{
$files = [];
if (!isset($this->data['info']['files'])) {
// Single-file torrent
@@ -310,8 +340,7 @@ class BencodeTorrent extends Bencode {
$this->data['info']['name']);
$size = $this->data['info']['length'];
$files[] = ['path' => $name, 'size' => $size];
}
else {
} else {
$size = 0;
foreach ($this->data['info']['files'] as $file) {
$size += $file['length'];
@@ -328,11 +357,13 @@ class BencodeTorrent extends Bencode {
return array('total_size' => $size, 'files' => $files);
}
public function hasFiles(): bool {
public function hasFiles(): bool
{
return isset($this->data['info']['files']);
}
public function hasEncryptedFiles(): bool {
public function hasEncryptedFiles(): bool
{
return isset($this->data['encrypted_files']);
}
@@ -346,7 +377,8 @@ class BencodeTorrent extends Bencode {
*
* @return array
*/
public function getGazelleFileList() : array {
public function getGazelleFileList(): array
{
$files = [];
foreach ($this->getFileList()['files'] as $file) {
$path = $file['path'];
@@ -368,7 +400,8 @@ class BencodeTorrent extends Bencode {
*
* @return string
*/
private function makeUTF8(string $str) : string {
private function makeUTF8(string $str): string
{
if (preg_match('//u', $str)) {
$encoding = 'UTF-8';
}
@@ -383,8 +416,7 @@ class BencodeTorrent extends Bencode {
// @codeCoverageIgnoreEnd
if ($encoding === 'UTF-8') {
return $str;
}
else {
} else {
return @mb_convert_encoding($str, 'UTF-8', $encoding);
}
}
+21 -16
View File
@@ -1,9 +1,13 @@
<?php
declare(strict_types=1);
namespace OrpheusNET\BencodeTorrent;
class BencodeTest extends \PHPUnit\Framework\TestCase {
public function dataProvider() {
class BencodeTest extends \PHPUnit\Framework\TestCase
{
public static function dataProvider(): array
{
return [
['i0e', 0],
['i-1e', -1],
@@ -18,50 +22,51 @@ class BencodeTest extends \PHPUnit\Framework\TestCase {
/**
* @dataProvider dataProvider
* @param string $bencoded_string
* @param mixed $expected
*/
public function testDecodeEncode($bencoded_string, $expected) {
public function testDecodeEncode(string $bencoded_string, $expected): void
{
$bencode = new Bencode();
$bencode->decodeString($bencoded_string);
$this->assertEquals($expected, $bencode->getData());
$this->assertEquals($bencoded_string, $bencode->getEncode());
}
public function testEmptyDict() {
public function testEmptyDict(): void
{
$bencode = new Bencode();
$bencode->decodeString('de');
$this->assertEquals([], $bencode->getData());
$this->assertEquals('le', $bencode->getEncode());
}
/**
* @expectedException \RuntimeException
*/
public function testInvalidDictionaryKey() {
public function testInvalidDictionaryKey(): void
{
$bencode = new Bencode();
$this->expectException(\RuntimeException::class);
$bencode->decodeString('di1e5:valuee');
}
public function invalidIntegers() {
public static function invalidIntegers(): array
{
return [['-0'], ['a'], ['1.0']];
}
/**
* @param string $value
* @dataProvider invalidIntegers
* @expectedException \RuntimeException
*/
public function testInvalidInteger(string $value) {
public function testInvalidInteger(string $value): void
{
$bencode = new Bencode();
$this->expectException(\RuntimeException::class);
$bencode->decodeString("i{$value}e");
}
/**
* @expectedException \RuntimeException
*/
public function testInvalidString() {
public function testInvalidString(): void
{
$bencode = new Bencode();
$this->expectException(\RuntimeException::class);
$bencode->decodeString('i1e0e');
}
}
+94 -37
View File
@@ -1,14 +1,17 @@
<?php
declare(strict_types=1);
namespace OrpheusNET\BencodeTorrent;
class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
public function testLoadTorrent() {
class BencodeTorrentTest extends \PHPUnit\Framework\TestCase
{
public function testLoadTorrent(): void
{
$bencode = new BencodeTorrent();
try {
$bencode->decodeFile(__DIR__.'/data/test_1.torrent');
}
catch (\Exception $exc) {
$bencode->decodeFile(__DIR__ . '/data/test_1.torrent');
} catch (\Exception $exc) {
$this->fail('Decode should not have thrown exception');
}
$data = $bencode->getData();
@@ -83,11 +86,11 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$this->assertEquals(16020, strlen($data['info']['pieces']));
$this->assertEquals(1, $data['info']['private']);
$this->assertEquals('APL', $data['info']['source']);
$this->assertStringEqualsFile(__DIR__.'/data/test_1.torrent', $bencode->getEncode());
$this->assertStringEqualsFile(__DIR__ . '/data/test_1.torrent', $bencode->getEncode());
$this->assertEquals(104916260, $bencode->getSize());
$this->assertTrue($bencode->isPrivate());
$bencode->decodeString($bencode->getEncode());
$this->assertStringEqualsFile(__DIR__.'/data/test_1.torrent', $bencode->getEncode());
$this->assertStringEqualsFile(__DIR__ . '/data/test_1.torrent', $bencode->getEncode());
$this->assertEquals('1f830103427029a88dd5fde85be74e622ee07951', $bencode->getInfoHash());
$this->assertEquals($bencode->getInfoHash(), unpack('H*', $bencode->getHexInfoHash())[1]);
$this->assertFalse($bencode->hasEncryptedFiles());
@@ -141,9 +144,11 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
]
];
$this->assertEquals($file_list, $bencode->getFileList());
$this->assertSame(1425699508, $bencode->getCreationDate());
}
public function testSetData() {
public function testSetData(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -175,7 +180,8 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
);
}
public function testEmptyDictionary() {
public function testEmptyDictionary(): void
{
$expected = [
'encoding' => 'UTF8',
'info' => [
@@ -187,13 +193,14 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
];
$bencode = new BencodeTorrent();
$bencode->decodeString(
'd8:encoding4:UTF84:infod4:name4:test6:lengthi1213134e'.
'd8:encoding4:UTF84:infod4:name4:test6:lengthi1213134e' .
'6:pieces18:fake pieces stringe4:test0:e'
);
$this->assertEquals($expected, $bencode->getData());
}
public function testClean() {
public function testClean(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -226,7 +233,8 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$this->assertEquals($data, $bencode->getData());
}
public function testSetPrivate() {
public function testSetPrivate(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -246,7 +254,8 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$this->assertEquals(1, $actual['info']['private']);
}
public function testSetAlreadyPrivate() {
public function testSetAlreadyPrivate(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -260,7 +269,8 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$this->assertFalse($bencode->makePrivate());
}
public function testSetSource() {
public function testSetSource(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -272,14 +282,17 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
];
$bencode = new BencodeTorrent();
$bencode->setData($data);
$this->assertNull($bencode->getSource());
$this->assertTrue($bencode->setSource('APL'));
$actual = $bencode->getData();
$this->assertEquals('APL', $actual['info']['source']);
$this->assertSame('APL', $bencode->getSource());
$this->assertFalse($bencode->setSource('APL'));
$this->assertNotContains('unique', $actual['info']);
$this->assertNotContains('x_cross_seed', $actual['info']);
}
public function testSetAlreadySourced() {
public function testSetAlreadySourced(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -297,7 +310,8 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$this->assertEquals($data, $bencode->getData());
}
public function testSetDifferentSource() {
public function testSetDifferentSource(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -317,7 +331,8 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$this->assertNotContains('x_cross_seed', $actual['info']);
}
public function testSetValue() {
public function testSetValue(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -374,7 +389,8 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$this->assertTrue($bencode->hasFiles());
}
public function testGetUtf8Name() {
public function testGetUtf8Name(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -403,7 +419,8 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$this->assertEquals(['. s12345s test!! ÷'], $bencode->getGazelleFileList());
}
public function testIso88591Name() {
public function testIso88591Name(): void
{
$data = [
'info' => [
'length' => 1234,
@@ -416,19 +433,16 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$this->assertFalse($bencode->hasFiles());
}
/**
* @expectedException \RuntimeException
*/
public function testInvalidSet() {
public function testInvalidSet(): void
{
$data = ['encoding' => 'UTF8', 'announce' => 'http://localhost:8080/announce'];
$bencode = new BencodeTorrent();
$this->expectException(\RuntimeException::class);
$bencode->setData($data);
}
/**
* @expectedException \RuntimeException
*/
public function testInvalidSetValue() {
public function testInvalidSetValue(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
@@ -440,28 +454,29 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
$bencode = new BencodeTorrent();
$bencode->setData($data);
$this->expectException(\RuntimeException::class);
$bencode->setValue(['info' => []]);
}
/**
* @expectedException \RuntimeException
*/
public function testGetEncodeNoData() {
public function testGetEncodeNoData(): void
{
$bencode = new BencodeTorrent();
$this->expectException(\RuntimeException::class);
$bencode->getEncode();
}
/**
* @expectedException \RuntimeException
*/
public function testInvalidPath() {
public function testEmptyPathPart(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
'files' => [
0 => [
'length' => 1234,
'path' => ['']
'path' => [
'',
'01 - test test',
]
]
],
'name' => 'test',
@@ -471,5 +486,47 @@ class BencodeTorrentTest extends \PHPUnit\Framework\TestCase {
];
$bencode = new BencodeTorrent();
$bencode->setData($data);
$this->assertSame(
[
'total_size' => 1234,
'files' => [
0 => [
'path' => '/01 - test test',
'size' => 1234,
],
],
],
$bencode->getFileList()
);
}
public function testEmptyPath(): void
{
$data = [
'encoding' => 'UTF8',
'info' => [
'files' => [
0 => [
'length' => 1234,
'path' => [
'',
]
]
],
'name' => 'test',
'length' => 1213134,
'pieces' => 'fake pieces string'
]
];
$bencode = new BencodeTorrent();
$this->expectException(\RuntimeException::class);
$bencode->setData($data);
}
public function testValidateEmptyData(): void
{
$bencode = new BencodeTorrent();
$this->expectException(\TypeError::class);
$bencode->validate();
}
}