Import random/randomizer tests

master
Anton Smirnov 2022-09-14 21:48:03 +03:00
parent 6dedd00d98
commit 5fe4ee3500
30 changed files with 291 additions and 510 deletions

11
debug/gh-9186.php Normal file
View File

@ -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";}}}}'
)
);

View File

@ -28,6 +28,10 @@ Serialization
unserializable by the native extension.
* Serialization in PHP 7.1 - 7.3 will trigger a warning.
Silence it with ``@`` if you don't care.
* Does not throw in GH-9186_ case but also does not create a dynamic property (PHP 7.4+)
Undefined behavior for PHP <7.4 (returns false with a warning for the given code)
.. _GH-9186: https://github.com/php/php-src/issues/9186
Randomizer
----------

View File

@ -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);
}
}

View File

@ -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 */
}
}

View File

@ -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++];
}
}

View File

@ -9,7 +9,7 @@ use Random\Engine;
/**
* @see https://github.com/php/php-src/blob/master/ext/random/tests/02_engine/all_serialize_user.phpt
*/
class TestCountingEngine32 implements Engine
class CountingEngine32 implements Engine
{
/** @var int */
private $count = 0;

View File

@ -2,14 +2,14 @@
declare(strict_types=1);
namespace Arokettu\Random\Tests\DevEngines;
namespace Arokettu\Random\Tests\DevEngines\FromPHP;
use Random\Engine;
/**
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/user_unsafe.phpt
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt
*/
final class EmptyStringEngine implements Engine
class EmptyStringEngine implements Engine
{
public function generate(): string
{

View File

@ -6,7 +6,10 @@ namespace Arokettu\Random\Tests\DevEngines\FromPHP;
use Random\Engine;
class TestGetBytesEngine implements Engine
/**
* https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/methods/getBytes_expansion.phpt
*/
class GetBytesExpansionEngine implements Engine
{
/** @var int */
private $count = 0;

View File

@ -2,12 +2,12 @@
declare(strict_types=1);
namespace Arokettu\Random\Tests\DevEngines;
namespace Arokettu\Random\Tests\DevEngines\FromPHP;
use Random\Engine;
/**
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/user_unsafe.phpt
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt
*/
class HeavilyBiasedEngine implements Engine
{

View File

@ -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++];
}
}

View File

@ -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');
}
}

View File

@ -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;
}
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Engine;
use Arokettu\Random\Tests\DevEngines\FromPHP\TestCountingEngine32;
use Arokettu\Random\Tests\DevEngines\FromPHP\CountingEngine32;
use Arokettu\Random\Tests\DevEngines\FromPHP\TestShaEngine;
use PHPUnit\Framework\TestCase;
@ -16,7 +16,7 @@ class AllSerializeUserTest extends TestCase
public function testSerialize(): void
{
$engines = [];
$engines[] = new TestCountingEngine32();
$engines[] = new CountingEngine32();
$engines[] = new TestShaEngine();
foreach ($engines as $engine) {

View File

@ -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));
}
}
}

View File

@ -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
}

View File

@ -9,22 +9,32 @@ use Random\Engine\Mt19937;
use Random\Randomizer;
/**
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/compatibility_mt.phpt
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/compatibility_mt_rand.phpt
*/
class CompatibilityMtTest extends TestCase
class CompatibilityMtRandTest extends TestCase
{
public function testCompatibility(): void
{
$randomizer = new Randomizer(new Mt19937(1234, \MT_RAND_PHP));
\mt_srand(1234, \MT_RAND_PHP);
for ($i = 0; $i < 1000; $i++) {
for ($i = 0; $i < 10000; $i++) {
self::assertEquals(\mt_rand(), $randomizer->nextInt());
}
for ($i = 0; $i < 10000; $i++) {
self::assertEquals(\mt_rand(0, $i), $randomizer->getInt(0, $i));
}
$randomizer = new Randomizer(new Mt19937(1234, \MT_RAND_MT19937));
\mt_srand(1234, \MT_RAND_MT19937);
for ($i = 0; $i < 1000; $i++) {
for ($i = 0; $i < 10000; $i++) {
self::assertEquals(\mt_rand(), $randomizer->nextInt());
}
for ($i = 0; $i < 10000; $i++) {
self::assertEquals(\mt_rand(0, $i), $randomizer->getInt(0, $i));
}
}
}

View File

@ -4,12 +4,11 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
use Arokettu\Random\Tests\DevEngines\FromPHP\TestWrapperEngine;
use PHPUnit\Framework\TestCase;
use Random\Engine;
use Random\Engine\Mt19937;
use Random\Engine\PcgOneseq128XslRr64;
use Random\Engine\Xoshiro256StarStar;
use Random\RandomException;
use Random\Randomizer;
/**
@ -19,81 +18,21 @@ class CompatibilityUserTest extends TestCase
{
public function testCompatibility(): void
{
$native_randomizer = new Randomizer(new Mt19937(1234));
$user_randomizer = new Randomizer(new class () implements Engine {
/** @var Engine */
private $engine;
$engines = [];
$engines[] = new Mt19937(1234);
$engines[] = new PcgOneseq128XslRr64(1234);
$engines[] = new Xoshiro256StarStar(1234);
public function __construct()
{
$this->engine = new Mt19937(1234);
}
foreach ($engines as $engine) {
$native_randomizer = new Randomizer(clone $engine);
$user_randomizer = new Randomizer(new TestWrapperEngine(clone $engine));
public function generate(): string
{
return $this->engine->generate();
}
});
for ($i = 0; $i < 1000; $i++) {
$native = $native_randomizer->nextInt();
$user = $user_randomizer->nextInt();
self::assertEquals($native, $user);
}
for ($i = 0; $i < 10000; $i++) {
$native = $native_randomizer->getInt(0, $i);
$user = $user_randomizer->getInt(0, $i);
try {
$native_randomizer = new Randomizer(new PcgOneseq128XslRr64(1234));
$user_randomizer = new Randomizer(new class () implements Engine {
/** @var Engine */
private $engine;
public function __construct()
{
$this->engine = new PcgOneseq128XslRr64(1234);
}
public function generate(): string
{
return $this->engine->generate();
}
});
for ($i = 0; $i < 1000; $i++) {
$native = $native_randomizer->nextInt();
$user = $user_randomizer->nextInt();
self::assertEquals($native, $user);
}
} catch (RandomException $e) {
if ($e->getMessage() !== 'Generated value exceeds size of int') {
throw $e;
}
}
try {
$native_randomizer = new Randomizer(new Xoshiro256StarStar(1234));
$user_randomizer = new Randomizer(new class () implements Engine {
/** @var Engine */
private $engine;
public function __construct()
{
$this->engine = new Xoshiro256StarStar(1234);
}
public function generate(): string
{
return $this->engine->generate();
}
});
for ($i = 0; $i < 1000; $i++) {
$native = $native_randomizer->nextInt();
$user = $user_randomizer->nextInt();
self::assertEquals($native, $user);
}
} catch (RandomException $e) {
if ($e->getMessage() !== 'Generated value exceeds size of int') {
throw $e;
}
}
}
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
use Arokettu\Random\Tests\DevEngines\ConstructTwiceTestUserEngine;
use Arokettu\Random\Tests\DevEngines\FromPHP\TestShaEngine;
use Error;
use PHPUnit\Framework\TestCase;
use Random\Engine\PcgOneseq128XslRr64;
@ -62,8 +62,8 @@ class ConstructTwiceTest extends TestCase
public function test3(): void
{
try {
$r = new Randomizer(new ConstructTwiceTestUserEngine());
$r->__construct(new ConstructTwiceTestUserEngine());
$r = new Randomizer(new TestShaEngine('1234'));
$r->__construct(new TestShaEngine('1234'));
} catch (Throwable $e) {
self::assertEquals(Error::class, \get_class($e));
self::assertEquals(
@ -83,7 +83,7 @@ class ConstructTwiceTest extends TestCase
{
try {
$r = new Randomizer(new Xoshiro256StarStar());
$r->__construct(new ConstructTwiceTestUserEngine());
$r->__construct(new TestShaEngine('1234'));
} catch (Throwable $e) {
self::assertEquals(Error::class, \get_class($e));
self::assertEquals(

View File

@ -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()
}
}

View File

@ -4,8 +4,7 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
use Arokettu\Random\Tests\DevEngines\EmptyStringEngine;
use Arokettu\Random\Tests\DevEngines\HeavilyBiasedEngine;
use Arokettu\Random\Tests\DevEngines\FromPHP\EmptyStringEngine;
use PHPUnit\Framework\TestCase;
use Random\BrokenRandomEngineError;
use Random\Randomizer;
@ -13,9 +12,9 @@ use RuntimeException;
use Throwable;
/**
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/user_unsafe.phpt
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt
*/
class UserUnsafeTest extends TestCase
class EngineUnsafeEmptyStringTest extends TestCase
{
public function testEmptyStringEngineGetInt(): void
{
@ -77,7 +76,7 @@ class UserUnsafeTest extends TestCase
public function testEmptyStringEngineShuffleArray(): void
{
try {
(new Randomizer(new EmptyStringEngine()))->shuffleArray(\range(1, 10));
(new Randomizer(new EmptyStringEngine()))->shuffleArray(\range(1, 1234));
} catch (Throwable $e) {
self::assertEquals(BrokenRandomEngineError::class, \get_class($e));
self::assertEquals(
@ -112,74 +111,4 @@ class UserUnsafeTest extends TestCase
throw new RuntimeException('Throwable expected'); // do not use expectException to test getPrevious()
}
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, 10));
} 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()
}
}

View File

@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
/**
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/user_exits.phpt
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/engine_unsafe_exits.phpt
*/
class UserExitsTest
class EngineUnsafeExitsTest
{
// Let's trust the engine here
}

View File

@ -4,28 +4,21 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
use Arokettu\Random\Tests\DevEngines\FromPHP\ThrowingEngine;
use Exception;
use PHPUnit\Framework\TestCase;
use Random\Engine;
use Random\Randomizer;
use RuntimeException;
use Throwable;
/**
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/user_throws.phpt
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/engine_unsafe_throws.phpt
*/
class UserThrowsTest extends TestCase
class EngineUnsafeThrowsTest extends TestCase
{
public function testEngineThrows(): void
{
$randomizer = (new Randomizer(
new class () implements Engine {
public function generate(): string
{
throw new Exception('Error');
}
}
));
$randomizer = (new Randomizer(new ThrowingEngine()));
try {
$randomizer->getBytes(1);

View File

@ -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))));
}
}

View File

@ -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)
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Randomizer\Methods;
use Arokettu\Random\Tests\DevEngines\FromPHP\TestGetBytesEngine;
use Arokettu\Random\Tests\DevEngines\FromPHP\GetBytesExpansionEngine;
use PHPUnit\Framework\TestCase;
use Random\Randomizer;
@ -15,7 +15,7 @@ class GetBytesExpansionTest extends TestCase
{
public function testGetBytes(): void
{
$randomizer = new Randomizer(new TestGetBytesEngine());
$randomizer = new Randomizer(new GetBytesExpansionEngine());
self::assertEquals('Hello', $randomizer->getBytes(5));
// Returned values are truncated to 64-bits for technical reasons, thus dropping i-z

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Randomizer\Methods;
use Arokettu\Random\Tests\DevEngines\FromPHP\TestByteEngine;
use Arokettu\Random\Tests\DevEngines\FromPHP\ByteEngine;
use PHPUnit\Framework\TestCase;
use Random\Randomizer;
@ -15,7 +15,7 @@ class GetIntExpansion32 extends TestCase
{
public function testGetInt(): void
{
$randomizer = new Randomizer(new TestByteEngine());
$randomizer = new Randomizer(new ByteEngine());
self::assertEquals('01020300', \bin2hex(\pack('V', $randomizer->getInt(0, 0x00FFFFFF))));
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Randomizer\Methods;
use Arokettu\Random\Tests\DevEngines\FromPHP\TestByteEngine;
use Arokettu\Random\Tests\DevEngines\FromPHP\ByteEngine;
use PHPUnit\Framework\TestCase;
use Random\Randomizer;
@ -20,7 +20,7 @@ class GetIntExpansion64 extends TestCase
$this->markTestSkipped("It's a 64 bit test");
}
$randomizer = new Randomizer(new TestByteEngine());
$randomizer = new Randomizer(new ByteEngine());
self::assertEquals('0102030405060700', \bin2hex(\pack('V', $randomizer->getInt(0, 0x00FFFFFFFFFFFFFF))));
}

View File

@ -7,7 +7,7 @@ namespace Arokettu\Random\Tests\FromPHP\Randomizer;
use Error;
use PHPUnit\Framework\TestCase;
use Random\Engine\PcgOneseq128XslRr64;
use Random\Engine\Secure;
use Random\Engine\Xoshiro256StarStar;
use Random\Randomizer;
use RuntimeException;
use Throwable;
@ -15,19 +15,15 @@ use Throwable;
/**
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/readonly.phpt
*/
class ReadonlyTest extends TestCase
class ReadonlyTest
{
public function testReadonly(): void
{
$one = new Randomizer(
new PcgOneseq128XslRr64(1234)
);
$one_ng_clone = clone $one->engine;
self::assertEquals($one->engine->generate(), $one_ng_clone->generate());
$randomizer = new Randomizer(new PcgOneseq128XslRr64(1234));
$referenceRandomizer = new Randomizer(new PcgOneseq128XslRr64(1234));
try {
$one->engine = $one_ng_clone;
$randomizer->engine = new Xoshiro256StarStar(1234);
} catch (Throwable $e) {
self::assertEquals(Error::class, \get_class($e));
self::assertEquals(
@ -37,55 +33,9 @@ class ReadonlyTest extends TestCase
self::assertEquals(0, $e->getCode());
self::assertNull($e->getPrevious());
return;
}
throw new RuntimeException('Throwable expected'); // do not use expectException to test getPrevious()
}
public function testCloneSecure(): void
{
$two = new Randomizer(new Secure());
try {
$two_ng_clone = clone $two->engine;
} catch (Throwable $e) {
self::assertEquals(Error::class, \get_class($e));
self::assertEquals(
'Trying to clone an uncloneable object of class Random\Engine\Secure',
$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 testAssign(): void
{
$one = new Randomizer(
new PcgOneseq128XslRr64(1234)
);
$one_ng_clone = clone $one->engine;
$two = new Randomizer(
new Secure()
);
try {
$two->engine = $one_ng_clone;
} catch (Throwable $e) {
self::assertEquals(Error::class, \get_class($e));
self::assertEquals(
'Cannot modify readonly property Random\Randomizer::$engine',
$e->getMessage()
);
self::assertEquals(0, $e->getCode());
self::assertNull($e->getPrevious());
for ($i = 0; $i < 10000; $i++) {
self::assertEquals($referenceRandomizer->getInt(0, $i), $randomizer->getInt(0, $i));
}
return;
}

View File

@ -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()
}
}

View File

@ -4,16 +4,12 @@ declare(strict_types=1);
namespace Arokettu\Random\Tests\FromPHP\Randomizer;
use Arokettu\Random\Tests\DevEngines\SerializeTestUserEngine;
use Arokettu\Random\Tests\DevEngines\FromPHP\TestShaEngine;
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\Randomizer;
use RuntimeException;
use Throwable;
/**
* @see https://github.com/php/php-src/blob/master/ext/random/tests/03_randomizer/serialize.phpt
@ -23,91 +19,27 @@ class SerializeTest extends TestCase
public function testSerializeSuccess(): 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));
global $___serialize_test_generate;
$___serialize_test_generate = \random_bytes(16);
$engines[] = new SerializeTestUserEngine();
$engines[] = new Mt19937(1234, \MT_RAND_MT19937);
$engines[] = new Mt19937(1234, \MT_RAND_PHP);
$engines[] = new PcgOneseq128XslRr64(1234);
$engines[] = new Xoshiro256StarStar(1234);
$engines[] = new TestShaEngine("1234");
foreach ($engines as $engine) {
$randomizer = new Randomizer($engine);
$randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX);
$randomizer2 = \unserialize(@\serialize($randomizer));
self::assertEquals(
$randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX),
$randomizer2->getInt(\PHP_INT_MIN, \PHP_INT_MAX)
);
}
}
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()
}
public function testSerializeAnon(): void
{
global $___serialize_test_generate;
$___serialize_test_generate = \random_bytes(16);
$engine = new class () implements Engine {
public function generate(): string
{
/** @psalm-suppress InvalidGlobal */
global $___serialize_test_generate;
return $___serialize_test_generate;
for ($i = 0; $i < 10000; $i++) {
$randomizer->getInt(0, $i);
}
};
$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));
if (\method_exists(__CLASS__, 'assertMatchesRegularExpression')) {
self::assertMatchesRegularExpression(
"/Serialization of '.*@anonymous' is not allowed/",
$e->getMessage()
);
} else {
self::assertRegExp(
"/Serialization of '.*@anonymous' is not allowed/",
$e->getMessage()
for ($i = 0; $i < 10000; $i++) {
self::assertEquals(
$randomizer->getInt(0, $i),
$randomizer2->getInt(0, $i)
);
}
self::assertEquals(0, $e->getCode());
self::assertNull($e->getPrevious());
return;
}
throw new RuntimeException('Throwable expected'); // do not use expectException to test getPrevious()
}
}