Import random/randomizer tests
parent
6dedd00d98
commit
5fe4ee3500
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
var_dump(
|
||||
unserialize(
|
||||
'O:17:"Random\Randomizer":1:{i:0;a:2:{s:3:"foo";N;s:6:"engine";O:32:"Random\Engine\Xoshiro256StarStar":2:' .
|
||||
'{i:0;a:0:{}i:1;a:4:{i:0;s:16:"7520fbc2d6f8de46";i:1;s:16:"84d2d2b9d7ba0a34";i:2;s:16:"d975f36db6490b32";i:3;' .
|
||||
's:16:"c19991ee16785b94";}}}}'
|
||||
)
|
||||
);
|
@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\DevEngines;
|
||||
|
||||
use Random\Engine;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/basic.phpt
|
||||
*/
|
||||
class BasicTestUserEngine implements Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
return \random_bytes(16);
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\DevEngines;
|
||||
|
||||
use Random\Engine;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/construct_twice.phpt
|
||||
*/
|
||||
class ConstructTwiceTestUserEngine implements Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
return \random_bytes(4); /* 32-bit */
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\DevEngines\FromPHP;
|
||||
|
||||
use Random\Engine;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/methods/getInt_expansion_32.phpt
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/methods/getInt_expansion_64.phpt
|
||||
*/
|
||||
class ByteEngine implements Engine
|
||||
{
|
||||
/** @var int */
|
||||
public $count = 0;
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return "\x01\x02\x03\x04\x05\x06\x07\x08"[$this->count++];
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\DevEngines\FromPHP;
|
||||
|
||||
use Random\Engine;
|
||||
|
||||
class TestByteEngine implements Engine
|
||||
{
|
||||
/** @var int */
|
||||
public $count = 0;
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return "\x01\x02\x03\x04\x05\x06\x07\x08"[$this->count++];
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\DevEngines\FromPHP;
|
||||
|
||||
use Exception;
|
||||
use Random\Engine;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/engine_unsafe_throws.phpt
|
||||
*/
|
||||
class ThrowingEngine implements Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
throw new Exception('Error');
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\DevEngines;
|
||||
|
||||
use Random\Engine;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/serialize.phpt
|
||||
*/
|
||||
class SerializeTestUserEngine implements Engine
|
||||
{
|
||||
public function generate(): string
|
||||
{
|
||||
global $___serialize_test_generate;
|
||||
return $___serialize_test_generate;
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
|
||||
|
||||
use Arokettu\Random\Tests\DevEngines\BasicTestUserEngine;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Random\Engine;
|
||||
use Random\Engine\Mt19937;
|
||||
use Random\Engine\PcgOneseq128XslRr64;
|
||||
use Random\Engine\Secure;
|
||||
use Random\Engine\Xoshiro256StarStar;
|
||||
use Random\RandomException;
|
||||
use Random\Randomizer;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/basic.phpt
|
||||
*/
|
||||
class BasicTest extends TestCase
|
||||
{
|
||||
// Original test had 1000, but it's too heavy for the non-native lib
|
||||
// also, tests are mostly duplicates
|
||||
private const ITERATIONS = 100;
|
||||
|
||||
public function testBasic(): void
|
||||
{
|
||||
$engines = [];
|
||||
$engines[] = new Mt19937(\random_int(\PHP_INT_MIN, \PHP_INT_MAX), \MT_RAND_MT19937);
|
||||
$engines[] = new Mt19937(\random_int(\PHP_INT_MIN, \PHP_INT_MAX), \MT_RAND_PHP);
|
||||
$engines[] = new PcgOneseq128XslRr64(\random_int(\PHP_INT_MIN, \PHP_INT_MAX));
|
||||
$engines[] = new Xoshiro256StarStar(\random_int(\PHP_INT_MIN, \PHP_INT_MAX));
|
||||
$engines[] = new Secure();
|
||||
$engines[] = new class () implements Engine {
|
||||
public function generate(): string
|
||||
{
|
||||
return \random_bytes(16);
|
||||
}
|
||||
};
|
||||
$engines[] = new BasicTestUserEngine();
|
||||
|
||||
foreach ($engines as $engine) {
|
||||
$randomizer = new Randomizer($engine);
|
||||
|
||||
// nextInt
|
||||
for ($i = 0; $i < self::ITERATIONS; $i++) {
|
||||
try {
|
||||
$randomizer->nextInt();
|
||||
} catch (RandomException $e) {
|
||||
self::assertEquals('Generated value exceeds size of int', $e->getMessage(), \get_class($engine));
|
||||
}
|
||||
}
|
||||
|
||||
// getInt
|
||||
for ($i = 0; $i < self::ITERATIONS; $i++) {
|
||||
$result = $randomizer->getInt(-50, 50);
|
||||
self::assertGreaterThanOrEqual(-50, $result, \get_class($engine));
|
||||
self::assertLessThanOrEqual(50, $result, \get_class($engine));
|
||||
}
|
||||
|
||||
// getBytes
|
||||
for ($i = 0; $i < self::ITERATIONS; $i++) {
|
||||
$length = \random_int(1, 1024);
|
||||
self::assertEquals($length, \strlen($randomizer->getBytes($length)), \get_class($engine));
|
||||
}
|
||||
|
||||
// shuffleArray
|
||||
$array = \range(1, self::ITERATIONS);
|
||||
$shuffled_array = $randomizer->shuffleArray($array);
|
||||
self::assertNotEquals($array, $shuffled_array, \get_class($engine));
|
||||
|
||||
// shuffleBytes
|
||||
$string = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ' .
|
||||
'ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco ' .
|
||||
'laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in ' .
|
||||
'voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non ' .
|
||||
'proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
|
||||
$shuffled_string = $randomizer->shuffleBytes($string);
|
||||
self::assertNotEquals($string, $shuffled_string, \get_class($engine));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/compatibility_array_rand.phpt
|
||||
*/
|
||||
class CompatibilityArrayRandTest
|
||||
{
|
||||
// known incompatibility, skip
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
|
||||
|
||||
use Arokettu\Random\Tests\DevEngines\FromPHP\HeavilyBiasedEngine;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Random\BrokenRandomEngineError;
|
||||
use Random\Randomizer;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt
|
||||
*/
|
||||
class EngineUnsafeBiased extends TestCase
|
||||
{
|
||||
public function testHeavilyBiasedEngineGetInt(): void
|
||||
{
|
||||
try {
|
||||
(new Randomizer(new HeavilyBiasedEngine()))->getInt(0, 123);
|
||||
} catch (Throwable $e) {
|
||||
self::assertEquals(BrokenRandomEngineError::class, \get_class($e));
|
||||
self::assertEquals(
|
||||
'Failed to generate an acceptable random number in 50 attempts',
|
||||
$e->getMessage()
|
||||
);
|
||||
self::assertEquals(0, $e->getCode());
|
||||
self::assertNull($e->getPrevious());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException('Throwable expected'); // do not use expectException to test getPrevious()
|
||||
}
|
||||
|
||||
public function testHeavilyBiasedEngineNextInt(): void
|
||||
{
|
||||
$r = (new Randomizer(new HeavilyBiasedEngine()))->nextInt();
|
||||
|
||||
self::assertEquals(\PHP_INT_MAX, $r);
|
||||
}
|
||||
|
||||
public function testHeavilyBiasedEngineGetBytes(): void
|
||||
{
|
||||
$r = (new Randomizer(new HeavilyBiasedEngine()))->getBytes(1);
|
||||
|
||||
self::assertEquals('ff', \bin2hex($r));
|
||||
}
|
||||
|
||||
public function testHeavilyBiasedEngineShuffleArray(): void
|
||||
{
|
||||
try {
|
||||
(new Randomizer(new HeavilyBiasedEngine()))->shuffleArray(\range(1, 1234));
|
||||
} catch (Throwable $e) {
|
||||
self::assertEquals(BrokenRandomEngineError::class, \get_class($e));
|
||||
self::assertEquals(
|
||||
'Failed to generate an acceptable random number in 50 attempts',
|
||||
$e->getMessage()
|
||||
);
|
||||
self::assertEquals(0, $e->getCode());
|
||||
self::assertNull($e->getPrevious());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException('Throwable expected'); // do not use expectException to test getPrevious()
|
||||
}
|
||||
|
||||
public function testHeavilyBiasedEngineShuffleBytes(): void
|
||||
{
|
||||
try {
|
||||
(new Randomizer(new HeavilyBiasedEngine()))->shuffleBytes('foobar');
|
||||
} catch (Throwable $e) {
|
||||
self::assertEquals(BrokenRandomEngineError::class, \get_class($e));
|
||||
self::assertEquals(
|
||||
'Failed to generate an acceptable random number in 50 attempts',
|
||||
$e->getMessage()
|
||||
);
|
||||
self::assertEquals(0, $e->getCode());
|
||||
self::assertNull($e->getPrevious());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException('Throwable expected'); // do not use expectException to test getPrevious()
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Random\Engine;
|
||||
use Random\Randomizer;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/get_int_user.phpt
|
||||
*/
|
||||
class GetIntUserTest extends TestCase
|
||||
{
|
||||
public function testGetInt(): void
|
||||
{
|
||||
$randomizer = new Randomizer(
|
||||
new class () implements Engine
|
||||
{
|
||||
/** @var int */
|
||||
public $count = 0;
|
||||
|
||||
public function generate(): string
|
||||
{
|
||||
return "\x01\x02\x03\x04\x05\x06\x07\x08"[$this->count++];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
self::assertEquals('01020300', \bin2hex(\pack('V', $randomizer->getInt(0, 0xFFFFFF))));
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
|
||||
|
||||
/**
|
||||
* GH-9186 strict-properties can be bypassed using unserialization
|
||||
* https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt
|
||||
*/
|
||||
class Gh9186UnserializeTest
|
||||
{
|
||||
// enforced in slightly incompatible way (doesn't throw)
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Random\Engine\Secure;
|
||||
use Random\Randomizer;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/serialize_disallowed.phpt
|
||||
*/
|
||||
class SerializeDisallowedTest extends TestCase
|
||||
{
|
||||
public function testSerializeSecure(): void
|
||||
{
|
||||
$engine = new Secure();
|
||||
|
||||
$randomizer = new Randomizer($engine);
|
||||
$randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX);
|
||||
|
||||
try {
|
||||
$randomizer2 = \unserialize(@\serialize($randomizer));
|
||||
} catch (Throwable $e) {
|
||||
self::assertEquals(\Exception::class, \get_class($e));
|
||||
self::assertEquals(
|
||||
"Serialization of 'Random\Engine\Secure' is not allowed",
|
||||
$e->getMessage()
|
||||
);
|
||||
self::assertEquals(0, $e->getCode());
|
||||
self::assertNull($e->getPrevious());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new RuntimeException('Throwable expected'); // do not use expectException to test getPrevious()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue