From ed3825f19709e39fb6a344b6da51bfab7c5e82d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 24 Aug 2022 00:50:32 +0200 Subject: [PATCH 1/3] Unify structure for ext/random's engine tests (2) This makes adjustments that were missed in 2d6a883b3a1e5ee4f225dce64584ad2522e8e391. --- .../tests/02_engine/all_serialize_native.phpt | 9 ++++--- .../tests/02_engine/all_serialize_user.phpt | 24 +++++-------------- .../pcgoneseq128xslrr64_jump_error.phpt | 2 +- .../tests/02_engine/user_compatibility.phpt | 8 +++++-- .../02_engine/xoshiro256starstar_seed.phpt | 12 +++++----- 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/ext/random/tests/02_engine/all_serialize_native.phpt b/ext/random/tests/02_engine/all_serialize_native.phpt index df56456d1338d..cadb208a66422 100644 --- a/ext/random/tests/02_engine/all_serialize_native.phpt +++ b/ext/random/tests/02_engine/all_serialize_native.phpt @@ -13,6 +13,8 @@ $engines[] = new PcgOneseq128XslRr64(1234); $engines[] = new Xoshiro256StarStar(1234); foreach ($engines as $engine) { + echo $engine::class, PHP_EOL; + for ($i = 0; $i < 10_000; $i++) { $engine->generate(); } @@ -21,9 +23,7 @@ foreach ($engines as $engine) { for ($i = 0; $i < 10_000; $i++) { if ($engine->generate() !== $engine2->generate()) { - $className = $engine::class; - - die("failure: {$className} at {$i}"); + die("failure: state differs at {$i}"); } } } @@ -32,4 +32,7 @@ die('success'); ?> --EXPECT-- +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar success diff --git a/ext/random/tests/02_engine/all_serialize_user.phpt b/ext/random/tests/02_engine/all_serialize_user.phpt index de3fe2f8292aa..94ed5e60aac14 100644 --- a/ext/random/tests/02_engine/all_serialize_user.phpt +++ b/ext/random/tests/02_engine/all_serialize_user.phpt @@ -5,17 +5,7 @@ Random: Engine: Serialization of user engines must preserve the sequence use Random\Engine; -final class User64 implements Engine -{ - private int $count = 0; - - public function generate(): string - { - return pack('P*', ++$this->count); - } -} - -final class User32 implements Engine +final class CountingEngine32 implements Engine { private int $count = 0; @@ -26,12 +16,11 @@ final class User32 implements Engine } $engines = []; -if (PHP_INT_SIZE >= 8) { - $engines[] = new User64(); -} -$engines[] = new User32(); +$engines[] = new CountingEngine32(); foreach ($engines as $engine) { + echo $engine::class, PHP_EOL; + for ($i = 0; $i < 10_000; $i++) { $engine->generate(); } @@ -40,9 +29,7 @@ foreach ($engines as $engine) { for ($i = 0; $i < 10_000; $i++) { if ($engine->generate() !== $engine2->generate()) { - $className = $engine::class; - - die("failure: {$className} at {$i}"); + die("failure: state differs at {$i}"); } } } @@ -51,4 +38,5 @@ die('success'); ?> --EXPECT-- +CountingEngine32 success diff --git a/ext/random/tests/02_engine/pcgoneseq128xslrr64_jump_error.phpt b/ext/random/tests/02_engine/pcgoneseq128xslrr64_jump_error.phpt index 02be8ecb6c92f..cbc6e370b1733 100644 --- a/ext/random/tests/02_engine/pcgoneseq128xslrr64_jump_error.phpt +++ b/ext/random/tests/02_engine/pcgoneseq128xslrr64_jump_error.phpt @@ -16,7 +16,7 @@ try { for ($i = 0; $i < 10_000; $i++) { if ($engine->generate() !== $referenceEngine->generate()) { - die('failure: state changed'); + die("failure: state differs at {$i}"); } } diff --git a/ext/random/tests/02_engine/user_compatibility.phpt b/ext/random/tests/02_engine/user_compatibility.phpt index 11ac451431204..ca55f71547d58 100644 --- a/ext/random/tests/02_engine/user_compatibility.phpt +++ b/ext/random/tests/02_engine/user_compatibility.phpt @@ -26,13 +26,14 @@ $engines[] = new PcgOneseq128XslRr64(1234); $engines[] = new Xoshiro256StarStar(1234); foreach ($engines as $engine) { + echo $engine::class, PHP_EOL; + $native_engine = clone $engine; $user_engine = new WrapperEngine(clone $engine); for ($i = 0; $i < 10_000; $i++) { if ($native_engine->generate() !== $user_engine->generate()) { - $className = $engine::class; - die("failure: {$className} at {$i}"); + die("failure: state differs at {$i}"); } } } @@ -41,4 +42,7 @@ die('success'); ?> --EXPECT-- +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar success diff --git a/ext/random/tests/02_engine/xoshiro256starstar_seed.phpt b/ext/random/tests/02_engine/xoshiro256starstar_seed.phpt index f4fa0a40b9898..ec31c358fd68e 100644 --- a/ext/random/tests/02_engine/xoshiro256starstar_seed.phpt +++ b/ext/random/tests/02_engine/xoshiro256starstar_seed.phpt @@ -6,17 +6,17 @@ Random: Engine: Xoshiro256StarStar: The seed parameter must work as expected use Random\Engine\Xoshiro256StarStar; echo "Random integer seed", PHP_EOL; -$engine = new Xoshiro256StarStar(\random_int(\PHP_INT_MIN, \PHP_INT_MAX)); +$engine = new Xoshiro256StarStar(random_int(PHP_INT_MIN, PHP_INT_MAX)); echo PHP_EOL, PHP_EOL; echo "Random string seed", PHP_EOL; -$engine = new Xoshiro256StarStar(\random_bytes(32)); +$engine = new Xoshiro256StarStar(random_bytes(32)); echo PHP_EOL, PHP_EOL; echo "Invalid data type", PHP_EOL; try { $engine = new Xoshiro256StarStar(1.0); -} catch (\Throwable $e) { +} catch (Throwable $e) { echo $e->getMessage(), PHP_EOL; } echo PHP_EOL, PHP_EOL; @@ -24,15 +24,15 @@ echo PHP_EOL, PHP_EOL; echo "Invalid string seed length", PHP_EOL; try { $engine = new Xoshiro256StarStar('foobar'); -} catch (\Throwable $e) { +} catch (Throwable $e) { echo $e->getMessage(), PHP_EOL; } echo PHP_EOL, PHP_EOL; echo "Null seed", PHP_EOL; try { - $engine = new Random\Engine\Xoshiro256StarStar(\str_repeat("\x00", 32)); -} catch (\Throwable $e) { + $engine = new Xoshiro256StarStar(str_repeat("\x00", 32)); +} catch (Throwable $e) { echo $e->getMessage(), PHP_EOL; } echo PHP_EOL, PHP_EOL; From 81ac90f25f6096e31a3207a174ccd8ce08be9163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 24 Aug 2022 15:42:08 +0200 Subject: [PATCH 2/3] Add `engines.inc` for ext/random tests --- .../tests/02_engine/all_serialize_user.phpt | 5 ++ .../tests/02_engine/user_compatibility.phpt | 15 +--- ext/random/tests/engines.inc | 74 +++++++++++++++++++ 3 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 ext/random/tests/engines.inc diff --git a/ext/random/tests/02_engine/all_serialize_user.phpt b/ext/random/tests/02_engine/all_serialize_user.phpt index 94ed5e60aac14..9ae742314f5af 100644 --- a/ext/random/tests/02_engine/all_serialize_user.phpt +++ b/ext/random/tests/02_engine/all_serialize_user.phpt @@ -4,6 +4,9 @@ Random: Engine: Serialization of user engines must preserve the sequence --EXPECT-- CountingEngine32 +Random\Engine\Test\TestShaEngine success diff --git a/ext/random/tests/02_engine/user_compatibility.phpt b/ext/random/tests/02_engine/user_compatibility.phpt index ca55f71547d58..7125061f6c9e5 100644 --- a/ext/random/tests/02_engine/user_compatibility.phpt +++ b/ext/random/tests/02_engine/user_compatibility.phpt @@ -6,19 +6,10 @@ Random: Engine: Native engines can be wrapped without changing their sequence use Random\Engine; use Random\Engine\Mt19937; use Random\Engine\PcgOneseq128XslRr64; +use Random\Engine\Test\TestWrapperEngine; use Random\Engine\Xoshiro256StarStar; -class WrapperEngine implements Engine -{ - public function __construct(private readonly Engine $engine) - { - } - - public function generate(): string - { - return $this->engine->generate(); - } -} +include __DIR__ . "/../engines.inc"; $engines = []; $engines[] = new Mt19937(1234); @@ -29,7 +20,7 @@ foreach ($engines as $engine) { echo $engine::class, PHP_EOL; $native_engine = clone $engine; - $user_engine = new WrapperEngine(clone $engine); + $user_engine = new TestWrapperEngine(clone $engine); for ($i = 0; $i < 10_000; $i++) { if ($native_engine->generate() !== $user_engine->generate()) { diff --git a/ext/random/tests/engines.inc b/ext/random/tests/engines.inc new file mode 100644 index 0000000000000..909f581d775a1 --- /dev/null +++ b/ext/random/tests/engines.inc @@ -0,0 +1,74 @@ +state = $state; + } else { + $this->state = random_bytes(20); + } + } + + public function generate(): string + { + $this->state = sha1($this->state, true); + + return substr($this->state, 0, 8); + } +} + +final class TestWrapperEngine implements Engine +{ + public function __construct(private readonly Engine $engine) + { + } + + public function generate(): string + { + return $this->engine->generate(); + } +} + +final class TestXoshiro128PlusPlusEngine implements Engine +{ + public function __construct( + private int $s0, + private int $s1, + private int $s2, + private int $s3 + ) { + } + + private static function rotl($x, $k) + { + return (($x << $k) | ($x >> (32 - $k))) & 0xFFFFFFFF; + } + + public function generate(): string + { + $result = (self::rotl(($this->s0 + $this->s3) & 0xFFFFFFFF, 7) + $this->s0) & 0xFFFFFFFF; + + $t = ($this->s1 << 9) & 0xFFFFFFFF; + + $this->s2 ^= $this->s0; + $this->s3 ^= $this->s1; + $this->s1 ^= $this->s2; + $this->s0 ^= $this->s3; + + $this->s2 ^= $t; + + $this->s3 = self::rotl($this->s3, 11); + + return pack('V', $result); + } +} + +?> From 78dbe191c622cd585635d8e002274a7755e3cb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Wed, 24 Aug 2022 15:46:05 +0200 Subject: [PATCH 3/3] Unify structure for ext/random's randomizer tests --- .../tests/02_engine/user_compatibility.phpt | 2 +- ext/random/tests/03_randomizer/basic.phpt | 81 ---------- ...eys.phpt => compatibility_array_rand.phpt} | 22 ++- .../tests/03_randomizer/compatibility_mt.phpt | 24 --- .../03_randomizer/compatibility_mt_rand.phpt | 49 ++++++ .../03_randomizer/compatibility_user.phpt | 87 ++++------- .../tests/03_randomizer/construct_twice.phpt | 47 +++--- .../03_randomizer/engine_unsafe_biased.phpt | 58 +++++++ .../engine_unsafe_empty_string.phpt | 58 +++++++ .../03_randomizer/engine_unsafe_exits.phpt | 23 +++ .../03_randomizer/engine_unsafe_throws.phpt | 28 ++++ ext/random/tests/03_randomizer/get_bytes.phpt | 40 ----- .../tests/03_randomizer/get_int_user.phpt | 22 --- .../tests/03_randomizer/methods/getBytes.phpt | 47 ++++++ .../methods/getBytes_expansion.phpt | 43 ++++++ .../tests/03_randomizer/methods/getInt.phpt | 54 +++++++ .../methods/getInt_expansion_32.phpt | 25 ++++ .../methods/getInt_expansion_64.phpt | 27 ++++ .../getInt_gh9415.phpt} | 0 .../tests/03_randomizer/methods/nextInt.phpt | 50 +++++++ .../nextInt_64_engine_on_32_platform.phpt | 21 +++ .../03_randomizer/methods/pickArrayKeys.phpt | 84 +++++++++++ .../methods/pickArrayKeys_error.phpt | 49 ++++++ .../03_randomizer/methods/shuffleArray.phpt | 53 +++++++ .../03_randomizer/methods/shuffleBytes.phpt | 60 ++++++++ .../tests/03_randomizer/nextint_error.phpt | 18 --- .../03_randomizer/pick_array_keys_error.phpt | 52 ------- ext/random/tests/03_randomizer/readonly.phpt | 41 ++--- ext/random/tests/03_randomizer/serialize.phpt | 77 +++++----- .../03_randomizer/serialize_disallowed.phpt | 17 +++ .../tests/03_randomizer/user_exits.phpt | 19 --- .../tests/03_randomizer/user_throws.phpt | 24 --- .../tests/03_randomizer/user_unsafe.phpt | 141 ------------------ 33 files changed, 860 insertions(+), 583 deletions(-) delete mode 100644 ext/random/tests/03_randomizer/basic.phpt rename ext/random/tests/03_randomizer/{pick_array_keys.phpt => compatibility_array_rand.phpt} (73%) delete mode 100644 ext/random/tests/03_randomizer/compatibility_mt.phpt create mode 100644 ext/random/tests/03_randomizer/compatibility_mt_rand.phpt create mode 100644 ext/random/tests/03_randomizer/engine_unsafe_biased.phpt create mode 100644 ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt create mode 100644 ext/random/tests/03_randomizer/engine_unsafe_exits.phpt create mode 100644 ext/random/tests/03_randomizer/engine_unsafe_throws.phpt delete mode 100644 ext/random/tests/03_randomizer/get_bytes.phpt delete mode 100644 ext/random/tests/03_randomizer/get_int_user.phpt create mode 100644 ext/random/tests/03_randomizer/methods/getBytes.phpt create mode 100644 ext/random/tests/03_randomizer/methods/getBytes_expansion.phpt create mode 100644 ext/random/tests/03_randomizer/methods/getInt.phpt create mode 100644 ext/random/tests/03_randomizer/methods/getInt_expansion_32.phpt create mode 100644 ext/random/tests/03_randomizer/methods/getInt_expansion_64.phpt rename ext/random/tests/03_randomizer/{gh9415.phpt => methods/getInt_gh9415.phpt} (100%) create mode 100644 ext/random/tests/03_randomizer/methods/nextInt.phpt create mode 100644 ext/random/tests/03_randomizer/methods/nextInt_64_engine_on_32_platform.phpt create mode 100644 ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt create mode 100644 ext/random/tests/03_randomizer/methods/pickArrayKeys_error.phpt create mode 100644 ext/random/tests/03_randomizer/methods/shuffleArray.phpt create mode 100644 ext/random/tests/03_randomizer/methods/shuffleBytes.phpt delete mode 100644 ext/random/tests/03_randomizer/nextint_error.phpt delete mode 100644 ext/random/tests/03_randomizer/pick_array_keys_error.phpt create mode 100644 ext/random/tests/03_randomizer/serialize_disallowed.phpt delete mode 100644 ext/random/tests/03_randomizer/user_exits.phpt delete mode 100644 ext/random/tests/03_randomizer/user_throws.phpt delete mode 100644 ext/random/tests/03_randomizer/user_unsafe.phpt diff --git a/ext/random/tests/02_engine/user_compatibility.phpt b/ext/random/tests/02_engine/user_compatibility.phpt index 7125061f6c9e5..401541e7141b0 100644 --- a/ext/random/tests/02_engine/user_compatibility.phpt +++ b/ext/random/tests/02_engine/user_compatibility.phpt @@ -9,7 +9,7 @@ use Random\Engine\PcgOneseq128XslRr64; use Random\Engine\Test\TestWrapperEngine; use Random\Engine\Xoshiro256StarStar; -include __DIR__ . "/../engines.inc"; +require __DIR__ . "/../engines.inc"; $engines = []; $engines[] = new Mt19937(1234); diff --git a/ext/random/tests/03_randomizer/basic.phpt b/ext/random/tests/03_randomizer/basic.phpt deleted file mode 100644 index a2f6378a71eb1..0000000000000 --- a/ext/random/tests/03_randomizer/basic.phpt +++ /dev/null @@ -1,81 +0,0 @@ ---TEST-- -Random: Randomizer: basic ---FILE-- -nextInt(); - } catch (\Random\RandomException $e) { - if ($e->getMessage() !== 'Generated value exceeds size of int') { - die($engine::class . ": nextInt: failure: {$e->getMessage()}"); - } - } - } - - // getInt - for ($i = 0; $i < 1000; $i++) { - $result = $randomizer->getInt(-50, 50); - if ($result > 50 || $result < -50) { - die($engine::class . ': getInt: failure'); - } - } - - // getBytes - for ($i = 0; $i < 1000; $i++) { - $length = \random_int(1, 1024); - if (\strlen($randomizer->getBytes($length)) !== $length) { - die($engine::class . ': getBytes: failure'); - } - } - - // shuffleArray - $array = range(1, 1000); - $shuffled_array = $randomizer->shuffleArray($array); - (function () use ($array, $shuffled_array): void { - for ($i = 0; $i < count($array); $i++) { - if ($array[$i] !== $shuffled_array[$i]) { - return; - } - } - - die($engine::class . ': shuffleArray: failure'); - })(); - - // 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); - if ($string === $shuffled_string) { - die($engine::class . ': shuffleBytes: failure'); - } -} - -die('success'); -?> ---EXPECTF-- -success diff --git a/ext/random/tests/03_randomizer/pick_array_keys.phpt b/ext/random/tests/03_randomizer/compatibility_array_rand.phpt similarity index 73% rename from ext/random/tests/03_randomizer/pick_array_keys.phpt rename to ext/random/tests/03_randomizer/compatibility_array_rand.phpt index 168d01dbc42dd..364267faf6ba4 100644 --- a/ext/random/tests/03_randomizer/pick_array_keys.phpt +++ b/ext/random/tests/03_randomizer/compatibility_array_rand.phpt @@ -1,8 +1,11 @@ --TEST-- -Random: Randomizer: pickArrayKeys +Random: Randomizer: The Mt19937 engine and pickArrayKeys are consistent with array_rand() --FILE-- 0, 'bar' => 1, 'baz' => 2]; $list = range(1, 10); @@ -14,7 +17,7 @@ $mapPickTwoFunc = array_rand($map, 2); $listPickOneFunc = array_rand($list, 1); $listPickTwoFunc = array_rand($list, 2); -$randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(1234)); +$randomizer = new Randomizer(new Mt19937(1234)); [$mapPickOneMethod] = $randomizer->pickArrayKeys($map, 1); $mapPickTwoMethod = $randomizer->pickArrayKeys($map, 2); @@ -24,25 +27,30 @@ $listPickTwoMethod = $randomizer->pickArrayKeys($list, 2); if ($mapPickOneFunc !== $mapPickOneMethod) { var_dump($mapPickOneFunc, $mapPickOneMethod); - die('failure mapPickOne'); + + die('failure: mapPickOne'); } if ($mapPickTwoFunc !== $mapPickTwoMethod) { var_dump($mapPickTwoFunc, $mapPickTwoMethod); - die('failure mapPickTwo'); + + die('failure: mapPickTwo'); } if ($listPickOneFunc !== $listPickOneMethod) { var_dump($listPickOneFunc, $listPickOneMethod); - die('failure listPickOne'); + + die('failure: listPickOne'); } if ($listPickTwoFunc !== $listPickTwoMethod) { var_dump($listPickTwoFunc, $listPickOneMethod); - die('failure listPickTwo'); + + die('failure: listPickTwo'); } die('success'); + ?> ---EXPECTF-- +--EXPECT-- success diff --git a/ext/random/tests/03_randomizer/compatibility_mt.phpt b/ext/random/tests/03_randomizer/compatibility_mt.phpt deleted file mode 100644 index 9740a494d6fe6..0000000000000 --- a/ext/random/tests/03_randomizer/compatibility_mt.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -Random: Randomizer: Compatibility: Mt19937 ---FILE-- -nextInt() !== \mt_rand()) { - die('failure'); - } -} - -$randomizer = new \Random\Randomizer(new \Random\Engine\Mt19937(1234, \MT_RAND_MT19937)); -\mt_srand(1234, \MT_RAND_MT19937); -for ($i = 0; $i < 1000; $i++) { - if ($randomizer->nextInt() !== \mt_rand()) { - die('failure'); - } -} - -die('success'); ---EXPECT-- -success diff --git a/ext/random/tests/03_randomizer/compatibility_mt_rand.phpt b/ext/random/tests/03_randomizer/compatibility_mt_rand.phpt new file mode 100644 index 0000000000000..a69c50d0cb622 --- /dev/null +++ b/ext/random/tests/03_randomizer/compatibility_mt_rand.phpt @@ -0,0 +1,49 @@ +--TEST-- +Random: Randomizer: The Mt19937 engine is a drop-in replacement for mt_rand() +--FILE-- +nextInt() !== mt_rand()) { + die("failure: state differs at {$i} for nextInt()"); + } +} + +for ($i = 0; $i < 10_000; $i++) { + if ($randomizer->getInt(0, $i) !== mt_rand(0, $i)) { + die("failure: state differs at {$i} for getInt()"); + } +} + +echo "MT_RAND_MT19937", PHP_EOL; + +$randomizer = new Randomizer(new Mt19937(1234, MT_RAND_MT19937)); +mt_srand(1234, MT_RAND_MT19937); + +for ($i = 0; $i < 10_000; $i++) { + if ($randomizer->nextInt() !== mt_rand()) { + die("failure: state differs at {$i} for nextInt()"); + } +} + +for ($i = 0; $i < 10_000; $i++) { + if ($randomizer->getInt(0, $i) !== mt_rand(0, $i)) { + die("failure: state differs at {$i} for getInt()"); + } +} + +die('success'); + +?> +--EXPECT-- +MT_RAND_PHP +MT_RAND_MT19937 +success diff --git a/ext/random/tests/03_randomizer/compatibility_user.phpt b/ext/random/tests/03_randomizer/compatibility_user.phpt index 3b4251aecbe72..a4601b55ab04a 100644 --- a/ext/random/tests/03_randomizer/compatibility_user.phpt +++ b/ext/random/tests/03_randomizer/compatibility_user.phpt @@ -1,80 +1,43 @@ --TEST-- -Random: Randomizer: Compatibility: user +Random: Randomizer: Native engines can be wrapped without changing their sequence --FILE-- engine->generate(); - } -}); -for ($i = 0; $i < 1000; $i++) { - $native = $native_randomizer->nextInt(); - $user = $user_randomizer->nextInt(); - if ($native !== $user) { - die("failure Mt19937 i: {$i} native: {$native} user: {$user}"); - } -} +require __DIR__ . "/../engines.inc"; -try { - $native_randomizer = new \Random\Randomizer(new \Random\Engine\PcgOneseq128XslRr64(1234)); - $user_randomizer = new \Random\Randomizer(new class () implements \Random\Engine { - public function __construct(private $engine = new \Random\Engine\PcgOneseq128XslRr64(1234)) - { - } +$engines = []; +$engines[] = new Mt19937(1234); +$engines[] = new PcgOneseq128XslRr64(1234); +$engines[] = 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(); - if ($native !== $user) { - die("failure PcgOneseq128XslRr64 i: {$i} native: {$native} user: {$user}"); - } - } -} catch (\Random\RandomException $e) { - if ($e->getMessage() !== 'Generated value exceeds size of int') { - throw $e; - } -} +foreach ($engines as $engine) { + echo $engine::class, PHP_EOL; -try { - $native_randomizer = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar(1234)); - $user_randomizer = new \Random\Randomizer(new class () implements \Random\Engine { - public function __construct(private $engine = new \Random\Engine\Xoshiro256StarStar(1234)) - { - } + $native_randomizer = new Randomizer(clone $engine); + $user_randomizer = new Randomizer(new TestWrapperEngine(clone $engine)); + + for ($i = 0; $i < 10_000; $i++) { + $native = $native_randomizer->getInt(0, $i); + $user = $user_randomizer->getInt(0, $i); - public function generate(): string - { - return $this->engine->generate(); - } - }); - - for ($i = 0; $i < 1000; $i++) { - $native = $native_randomizer->nextInt(); - $user = $user_randomizer->nextInt(); if ($native !== $user) { - die("failure Xoshiro256StarStar i: {$i} native: {$native} user: {$user}"); + die("failure: state differs at {$i}"); } } -} catch (\Random\RandomException $e) { - if ($e->getMessage() !== 'Generated value exceeds size of int') { - throw $e; - } } die('success'); + ?> --EXPECT-- +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar success diff --git a/ext/random/tests/03_randomizer/construct_twice.phpt b/ext/random/tests/03_randomizer/construct_twice.phpt index ed2a667e2af11..7128338ade973 100644 --- a/ext/random/tests/03_randomizer/construct_twice.phpt +++ b/ext/random/tests/03_randomizer/construct_twice.phpt @@ -1,46 +1,47 @@ --TEST-- -Random: Randomizer: Disallow manually calling __construct +Random: Randomizer: Calling __construct() fails due to readonly $engine property --FILE-- __construct(); -} catch (\Error $e) { - echo $e->getMessage() . PHP_EOL; + (new Randomizer())->__construct(); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; } try { - $r = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar()); - $r->__construct(new \Random\Engine\PcgOneseq128XslRr64()); -} catch (\Error $e) { - echo $e->getMessage() . PHP_EOL; + $randomizer = new Randomizer(new Xoshiro256StarStar()); + $randomizer->__construct(new PcgOneseq128XslRr64()); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; } try { - $r = new \Random\Randomizer(new \UserEngine()); - $r->__construct(new \UserEngine()); -} catch (\Error $e) { - echo $e->getMessage() . PHP_EOL; + $randomizer = new Randomizer(new TestShaEngine("1234")); + $randomizer->__construct(new TestShaEngine("1234")); +} catch (Error $e) { + echo $e->getMessage(), PHP_EOL; } try { - $r = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar()); - $r->__construct(new \UserEngine()); -} catch (\Error $e) { + $randomizer = new Randomizer(new Xoshiro256StarStar()); + $randomizer->__construct(new TestShaEngine("1234")); +} catch (Error $e) { echo $e->getMessage(), PHP_EOL; } -var_dump($r->engine::class); +var_dump($randomizer->engine::class); die('success'); + ?> --EXPECT-- Cannot modify readonly property Random\Randomizer::$engine diff --git a/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt b/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt new file mode 100644 index 0000000000000..09fbd85b54eb0 --- /dev/null +++ b/ext/random/tests/03_randomizer/engine_unsafe_biased.phpt @@ -0,0 +1,58 @@ +--TEST-- +Random: Randomizer: Heavily biased engines are detected and rejected +--FILE-- +getInt(0, 1234)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->nextInt()); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(bin2hex(randomizer()->getBytes(1))); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->shuffleArray(range(1, 1234))); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->shuffleBytes('foobar')); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +Failed to generate an acceptable random number in 50 attempts +int(%d) +string(2) "ff" +Failed to generate an acceptable random number in 50 attempts +Failed to generate an acceptable random number in 50 attempts diff --git a/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt new file mode 100644 index 0000000000000..01bd293bc0508 --- /dev/null +++ b/ext/random/tests/03_randomizer/engine_unsafe_empty_string.phpt @@ -0,0 +1,58 @@ +--TEST-- +Random: Randomizer: Engines returning an empty string are detected and rejected +--FILE-- +getInt(0, 1234)); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->nextInt()); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(bin2hex(randomizer()->getBytes(1))); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->shuffleArray(range(1, 1234))); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->shuffleBytes('foobar')); +} catch (Random\BrokenRandomEngineError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +A random engine must return a non-empty string +A random engine must return a non-empty string +A random engine must return a non-empty string +A random engine must return a non-empty string +A random engine must return a non-empty string diff --git a/ext/random/tests/03_randomizer/engine_unsafe_exits.phpt b/ext/random/tests/03_randomizer/engine_unsafe_exits.phpt new file mode 100644 index 0000000000000..cb598872d9773 --- /dev/null +++ b/ext/random/tests/03_randomizer/engine_unsafe_exits.phpt @@ -0,0 +1,23 @@ +--TEST-- +Random: Randomizer: Engines calling exit() are gracefully handled +--FILE-- +getBytes(1)); + +?> +--EXPECT-- +Exit diff --git a/ext/random/tests/03_randomizer/engine_unsafe_throws.phpt b/ext/random/tests/03_randomizer/engine_unsafe_throws.phpt new file mode 100644 index 0000000000000..bfb679f81a201 --- /dev/null +++ b/ext/random/tests/03_randomizer/engine_unsafe_throws.phpt @@ -0,0 +1,28 @@ +--TEST-- +Random: Randomizer: Engines throwing an exception are gracefully handled +--FILE-- +getBytes(1)); + +?> +--EXPECTF-- +Fatal error: Uncaught Exception: Error in %s:%d +Stack trace: +#0 [internal function]: ThrowingEngine->generate() +#1 %s(%d): Random\Randomizer->getBytes(1) +#2 {main} + thrown in %s on line %d diff --git a/ext/random/tests/03_randomizer/get_bytes.phpt b/ext/random/tests/03_randomizer/get_bytes.phpt deleted file mode 100644 index c6b4a59475e72..0000000000000 --- a/ext/random/tests/03_randomizer/get_bytes.phpt +++ /dev/null @@ -1,40 +0,0 @@ ---TEST-- -Random: Randomizer: getBytes ---FILE-- -count > 5) { - die('overflow'); - } - - return match ($this->count++) { - 0 => 'H', - 1 => 'e', - 2 => 'll', - 3 => 'o', - 4 => 'abcdefghijklmnopqrstuvwxyz', // 208 bits - 5 => 'success', - default => \random_bytes(16), - }; - } - } -); - -echo $randomizer->getBytes(5) . PHP_EOL; // Hello - -echo $randomizer->getBytes(8) . PHP_EOL; // abcdefgh (64 bits) - -die($randomizer->getBytes(7)); - -?> ---EXPECTF-- -Hello -abcdefgh -success diff --git a/ext/random/tests/03_randomizer/get_int_user.phpt b/ext/random/tests/03_randomizer/get_int_user.phpt deleted file mode 100644 index d6c995314319c..0000000000000 --- a/ext/random/tests/03_randomizer/get_int_user.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -Random: Randomizer: User Engine results are correctly expanded for getInt() ---FILE-- -count++]; - } - } -); - -var_dump(bin2hex(pack('V', $randomizer->getInt(0, 0xFFFFFF)))); - -?> ---EXPECT-- -string(8) "01020300" diff --git a/ext/random/tests/03_randomizer/methods/getBytes.phpt b/ext/random/tests/03_randomizer/methods/getBytes.phpt new file mode 100644 index 0000000000000..a236e9746fa19 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getBytes.phpt @@ -0,0 +1,47 @@ +--TEST-- +Random: Randomizer: getBytes(): Basic functionality +--FILE-- +getBytes($i)) !== $i) { + die("failure: incorrect string length at {$i}"); + } + } +} + +die('success'); + +?> +--EXPECT-- +Random\Engine\Mt19937 +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar +Random\Engine\Secure +Random\Engine\Test\TestShaEngine +success diff --git a/ext/random/tests/03_randomizer/methods/getBytes_expansion.phpt b/ext/random/tests/03_randomizer/methods/getBytes_expansion.phpt new file mode 100644 index 0000000000000..750b7870e4001 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getBytes_expansion.phpt @@ -0,0 +1,43 @@ +--TEST-- +Random: Randomizer: getBytes(): Returned bytes are consistently expanded +--FILE-- +count++) { + 0 => 'H', + 1 => 'e', + 2 => 'll', + 3 => 'o', + 4 => 'abcdefghijklmnopqrstuvwxyz', + 5 => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', + 6 => 'success', + default => throw new \Exception('Unhandled'), + }; + } +} + +$randomizer = new Randomizer(new TestEngine()); + +// 0-3: "Hello" - Insufficient bytes are concatenated. +var_dump($randomizer->getBytes(5)); + +// 4-5: "abcdefghABC" - Returned values are truncated to 64-bits for technical reasons, thus dropping i-z. +var_dump($randomizer->getBytes(11)); + +// 6: "success" +var_dump($randomizer->getBytes(7)); + +?> +--EXPECT-- +string(5) "Hello" +string(11) "abcdefghABC" +string(7) "success" diff --git a/ext/random/tests/03_randomizer/methods/getInt.phpt b/ext/random/tests/03_randomizer/methods/getInt.phpt new file mode 100644 index 0000000000000..faa098ade1364 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getInt.phpt @@ -0,0 +1,54 @@ +--TEST-- +Random: Randomizer: getInt(): Basic functionality +--FILE-- +getInt(-$i, $i); + + if ($result > $i || $result < -$i) { + die("failure: out of range at {$i}"); + } + } + + // Test that extreme ranges do not throw. + for ($i = 0; $i < 10_000; $i++) { + $randomizer->getInt(PHP_INT_MIN, PHP_INT_MAX); + } +} + +die('success'); + +?> +--EXPECT-- +Random\Engine\Mt19937 +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar +Random\Engine\Secure +Random\Engine\Test\TestShaEngine +success diff --git a/ext/random/tests/03_randomizer/methods/getInt_expansion_32.phpt b/ext/random/tests/03_randomizer/methods/getInt_expansion_32.phpt new file mode 100644 index 0000000000000..6e93208138c5f --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getInt_expansion_32.phpt @@ -0,0 +1,25 @@ +--TEST-- +Random: Randomizer: getInt(): Returned values with insufficient bits are correctly expanded +--FILE-- +count++]; + } +} + +$randomizer = new Randomizer(new ByteEngine()); + +var_dump(bin2hex(pack('V', $randomizer->getInt(0, 0x00FF_FFFF)))); + +?> +--EXPECT-- +string(8) "01020300" diff --git a/ext/random/tests/03_randomizer/methods/getInt_expansion_64.phpt b/ext/random/tests/03_randomizer/methods/getInt_expansion_64.phpt new file mode 100644 index 0000000000000..87c6bfeafeb85 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/getInt_expansion_64.phpt @@ -0,0 +1,27 @@ +--TEST-- +Random: Randomizer: getInt(): Returned values with insufficient bits are correctly expanded (64 Bit) +--SKIPIF-- + +--FILE-- +count++]; + } +} + +$randomizer = new Randomizer(new ByteEngine()); + +var_dump(bin2hex(pack('P', $randomizer->getInt(0, 0x00FF_FFFF_FFFF_FFFF)))); + +?> +--EXPECT-- +string(16) "0102030405060700" diff --git a/ext/random/tests/03_randomizer/gh9415.phpt b/ext/random/tests/03_randomizer/methods/getInt_gh9415.phpt similarity index 100% rename from ext/random/tests/03_randomizer/gh9415.phpt rename to ext/random/tests/03_randomizer/methods/getInt_gh9415.phpt diff --git a/ext/random/tests/03_randomizer/methods/nextInt.phpt b/ext/random/tests/03_randomizer/methods/nextInt.phpt new file mode 100644 index 0000000000000..fd4460cb811ce --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/nextInt.phpt @@ -0,0 +1,50 @@ +--TEST-- +Random: Randomizer: nextInt(): Basic functionality +--FILE-- +nextInt(); + } catch (\Random\RandomException $e) { + if ($e->getMessage() !== 'Generated value exceeds size of int' || PHP_INT_SIZE !== 4) { + throw $e; + } + } + } +} + +die('success'); + +?> +--EXPECT-- +Random\Engine\Mt19937 +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar +Random\Engine\Secure +Random\Engine\Test\TestShaEngine +success diff --git a/ext/random/tests/03_randomizer/methods/nextInt_64_engine_on_32_platform.phpt b/ext/random/tests/03_randomizer/methods/nextInt_64_engine_on_32_platform.phpt new file mode 100644 index 0000000000000..2a408e566e0b4 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/nextInt_64_engine_on_32_platform.phpt @@ -0,0 +1,21 @@ +--TEST-- +Random: Randomizer: nextInt(): Throws for 64 bit engines on 32 bit platforms +--SKIPIF-- + +--FILE-- +nextInt()); +} catch (Random\RandomException $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Generated value exceeds size of int diff --git a/ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt b/ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt new file mode 100644 index 0000000000000..4178455898df4 --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/pickArrayKeys.phpt @@ -0,0 +1,84 @@ +--TEST-- +Random: Randomizer: pickArrayKeys(): Basic functionality +--FILE-- +pickArrayKeys($array1, $i); + + if (array_unique($result) !== $result) { + die("failure: duplicates returned at {$i} for array1"); + } + + if (array_diff($result, array_keys($array1)) !== []) { + die("failure: non-keys returned at {$i} for array1"); + } + + $result = $randomizer->pickArrayKeys($array2, $i); + + if (array_unique($result) !== $result) { + die("failure: duplicates returned at {$i} for array2"); + } + + if (array_diff($result, array_keys($array2)) !== []) { + die("failure: non-keys returned at {$i} for array2"); + } + + $result = $randomizer->pickArrayKeys($array3, $i); + + if (array_unique($result) !== $result) { + die("failure: duplicates returned at {$i} for array3"); + } + + if (array_diff($result, array_keys($array3)) !== []) { + die("failure: non-keys returned at {$i} for array3"); + } + } +} + +die('success'); + +?> +--EXPECT-- +Random\Engine\Mt19937 +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar +Random\Engine\Secure +Random\Engine\Test\TestShaEngine +success diff --git a/ext/random/tests/03_randomizer/methods/pickArrayKeys_error.phpt b/ext/random/tests/03_randomizer/methods/pickArrayKeys_error.phpt new file mode 100644 index 0000000000000..15526327b095c --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/pickArrayKeys_error.phpt @@ -0,0 +1,49 @@ +--TEST-- +Random: Randomizer: pickArrayKeys(): Parameters are correctly validated +--FILE-- +pickArrayKeys("foo", 2)); +} catch (TypeError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->pickArrayKeys([], 0)); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->pickArrayKeys(range(1, 3), 0)); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->pickArrayKeys(range(1, 3), -1)); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + var_dump(randomizer()->pickArrayKeys(range(1, 3), 10)); +} catch (ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +Random\Randomizer::pickArrayKeys(): Argument #1 ($array) must be of type array, string given +Random\Randomizer::pickArrayKeys(): Argument #1 ($array) cannot be empty +Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array) +Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array) +Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array) diff --git a/ext/random/tests/03_randomizer/methods/shuffleArray.phpt b/ext/random/tests/03_randomizer/methods/shuffleArray.phpt new file mode 100644 index 0000000000000..c0f6d17fecddf --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/shuffleArray.phpt @@ -0,0 +1,53 @@ +--TEST-- +Random: Randomizer: shuffleArray(): Basic functionality +--FILE-- +shuffleArray($array); + + sort($result); + + if ($result !== $array) { + die("failure: not a permutation at {$i}"); + } + } +} + +die('success'); + +?> +--EXPECT-- +Random\Engine\Mt19937 +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar +Random\Engine\Secure +Random\Engine\Test\TestShaEngine +success diff --git a/ext/random/tests/03_randomizer/methods/shuffleBytes.phpt b/ext/random/tests/03_randomizer/methods/shuffleBytes.phpt new file mode 100644 index 0000000000000..d16168d32fe2d --- /dev/null +++ b/ext/random/tests/03_randomizer/methods/shuffleBytes.phpt @@ -0,0 +1,60 @@ +--TEST-- +Random: Randomizer: shuffleBytes(): Basic functionality +--FILE-- +shuffleBytes($bytes); + + $result = sort_bytes($result); + + if ($result !== $bytes) { + die("failure: not a permutation at {$i}"); + } + } +} + +die('success'); + +?> +--EXPECT-- +Random\Engine\Mt19937 +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar +Random\Engine\Secure +Random\Engine\Test\TestShaEngine +success diff --git a/ext/random/tests/03_randomizer/nextint_error.phpt b/ext/random/tests/03_randomizer/nextint_error.phpt deleted file mode 100644 index 5f9f8bdc77cf8..0000000000000 --- a/ext/random/tests/03_randomizer/nextint_error.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -Random: Randomizer: nextInt() throws for too large values on 32 Bit ---SKIPIF-- - ---FILE-- -nextInt()); -} catch (\Random\RandomException $e) { - echo $e->getMessage(), PHP_EOL; -} - -?> ---EXPECT-- -Generated value exceeds size of int diff --git a/ext/random/tests/03_randomizer/pick_array_keys_error.phpt b/ext/random/tests/03_randomizer/pick_array_keys_error.phpt deleted file mode 100644 index 98abc82a55018..0000000000000 --- a/ext/random/tests/03_randomizer/pick_array_keys_error.phpt +++ /dev/null @@ -1,52 +0,0 @@ ---TEST-- -Random: Randomizer: pickArrayKeys error pattern ---FILE-- -pickArrayKeys([], 0)); -} catch (\ValueError $e) { - echo $e->getMessage() . "\n"; -} - -try { - var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 0)); -} catch (\ValueError $e) { - echo $e->getMessage() . "\n"; -} - -try { - var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), -1)); -} catch (\ValueError $e) { - echo $e->getMessage() . "\n"; -} - -try { - var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 10)); -} catch (\ValueError $e) { - echo $e->getMessage() . "\n"; -} - -var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 3)); -var_dump((new \Random\Randomizer())->pickArrayKeys(range(1, 3), 2)); - -?> ---EXPECTF-- -Random\Randomizer::pickArrayKeys(): Argument #1 ($array) cannot be empty -Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array) -Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array) -Random\Randomizer::pickArrayKeys(): Argument #2 ($num) must be between 1 and the number of elements in argument #1 ($array) -array(3) { - [0]=> - int(%d) - [1]=> - int(%d) - [2]=> - int(%d) -} -array(2) { - [0]=> - int(%d) - [1]=> - int(%d) -} diff --git a/ext/random/tests/03_randomizer/readonly.phpt b/ext/random/tests/03_randomizer/readonly.phpt index f1f4a461714e4..f5cf7cc0dd5b0 100644 --- a/ext/random/tests/03_randomizer/readonly.phpt +++ b/ext/random/tests/03_randomizer/readonly.phpt @@ -1,43 +1,30 @@ --TEST-- -Random: Randomizer: readonly engine +Random: Randomizer: The engine property must be readonly --FILE-- engine; -if ($one->engine->generate() !== $one_ng_clone->generate()) { - die('invalid result'); -} +$randomizer = new Randomizer(new PcgOneseq128XslRr64(1234)); +$referenceRandomizer = new Randomizer(new PcgOneseq128XslRr64(1234)); try { - $one->engine = $one_ng_clone; -} catch (\Throwable $e) { - echo $e->getMessage() . PHP_EOL; + $randomizer->engine = new Xoshiro256StarStar(1234); +} catch (Throwable $e) { + echo $e->getMessage(), PHP_EOL; } -$two = new \Random\Randomizer( - new \Random\Engine\Secure() -); - -try { - $two_ng_clone = clone $two->engine; -} catch (\Throwable $e) { - echo $e->getMessage() . PHP_EOL; +for ($i = 0; $i < 10_000; $i++) { + if ($randomizer->getInt(0, $i) !== $referenceRandomizer->getInt(0, $i)) { + die("failure: state differs at {$i}"); + } } -try { - $two->engine = $one_ng_clone; -} catch (\Throwable $e) { - echo $e->getMessage() . PHP_EOL; -} +die('success'); -die('success') ?> --EXPECT-- Cannot modify readonly property Random\Randomizer::$engine -Trying to clone an uncloneable object of class Random\Engine\Secure -Cannot modify readonly property Random\Randomizer::$engine success diff --git a/ext/random/tests/03_randomizer/serialize.phpt b/ext/random/tests/03_randomizer/serialize.phpt index 37841a0db106f..b02bb93c827b3 100644 --- a/ext/random/tests/03_randomizer/serialize.phpt +++ b/ext/random/tests/03_randomizer/serialize.phpt @@ -1,57 +1,50 @@ --TEST-- -Random: Randomizer: serialize +Random: Randomizer: Serialization of the Randomizer must preserve the sequence --FILE-- getInt(\PHP_INT_MIN, \PHP_INT_MAX); - try { - $randomizer2 = unserialize(serialize($randomizer)); - } catch (\Exception $e) { - echo $e->getMessage() . PHP_EOL; - continue; - } + echo $engine::class, PHP_EOL; + + $randomizer = new Randomizer($engine); - if ($randomizer->getInt(\PHP_INT_MIN, \PHP_INT_MAX) !== $randomizer2->getInt(\PHP_INT_MIN, \PHP_INT_MAX)) { - die($engine::class . ': failure.'); + for ($i = 0; $i < 10_000; $i++) { + $randomizer->getInt(0, $i); } - echo $engine::class . ': success' . PHP_EOL; + $randomizer2 = unserialize(serialize($randomizer)); + + for ($i = 0; $i < 10_000; $i++) { + if ($randomizer->getInt(0, $i) !== $randomizer2->getInt(0, $i)) { + $className = $engine::class; + + die("failure: state differs at {$i}"); + } + } } die('success'); ?> ---EXPECTF-- -Random\Engine\Mt19937: success -Random\Engine\Mt19937: success -Random\Engine\PcgOneseq128XslRr64: success -Random\Engine\Xoshiro256StarStar: success -Serialization of 'Random\Engine\Secure' is not allowed -Serialization of 'Random\Engine@anonymous' is not allowed -UserEngine: success +--EXPECT-- +Random\Engine\Mt19937 +Random\Engine\Mt19937 +Random\Engine\PcgOneseq128XslRr64 +Random\Engine\Xoshiro256StarStar +Random\Engine\Test\TestShaEngine success diff --git a/ext/random/tests/03_randomizer/serialize_disallowed.phpt b/ext/random/tests/03_randomizer/serialize_disallowed.phpt new file mode 100644 index 0000000000000..06f8ea2cb09ae --- /dev/null +++ b/ext/random/tests/03_randomizer/serialize_disallowed.phpt @@ -0,0 +1,17 @@ +--TEST-- +Random: Randomizer: Serialization of the Randomizer fails if the engine is not serializable +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Serialization of 'Random\Engine\Secure' is not allowed diff --git a/ext/random/tests/03_randomizer/user_exits.phpt b/ext/random/tests/03_randomizer/user_exits.phpt deleted file mode 100644 index 6d6f5636cbec4..0000000000000 --- a/ext/random/tests/03_randomizer/user_exits.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -Random: Randomizer: User: Engine exits ---FILE-- -getBytes(1)); - -?> ---EXPECT-- -Exit diff --git a/ext/random/tests/03_randomizer/user_throws.phpt b/ext/random/tests/03_randomizer/user_throws.phpt deleted file mode 100644 index 41cd004b63879..0000000000000 --- a/ext/random/tests/03_randomizer/user_throws.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -Random: Randomizer: User: Engine throws ---FILE-- -getBytes(1)); - -?> ---EXPECTF-- -Fatal error: Uncaught Exception: Error in %s:%d -Stack trace: -#0 [internal function]: Random\Engine@anonymous->generate() -#1 %s(%d): Random\Randomizer->getBytes(1) -#2 {main} - thrown in %s on line %d diff --git a/ext/random/tests/03_randomizer/user_unsafe.phpt b/ext/random/tests/03_randomizer/user_unsafe.phpt deleted file mode 100644 index 6e6a8151fdf11..0000000000000 --- a/ext/random/tests/03_randomizer/user_unsafe.phpt +++ /dev/null @@ -1,141 +0,0 @@ ---TEST-- -Random: Randomizer: User: Engine unsafe ---FILE-- -getInt(0, 123)); - } catch (Throwable $e) { - echo $e, PHP_EOL; - } - - echo PHP_EOL, "-------", PHP_EOL, PHP_EOL; - - try { - var_dump((new Randomizer(new $engine()))->nextInt()); - } catch (Throwable $e) { - echo $e, PHP_EOL; - } - - echo PHP_EOL, "-------", PHP_EOL, PHP_EOL; - - try { - var_dump(bin2hex((new Randomizer(new $engine()))->getBytes(1))); - } catch (Throwable $e) { - echo $e, PHP_EOL; - } - - echo PHP_EOL, "-------", PHP_EOL, PHP_EOL; - - try { - var_dump((new Randomizer(new $engine()))->shuffleArray(\range(1, 10))); - } catch (Throwable $e) { - echo $e, PHP_EOL; - } - - echo PHP_EOL, "-------", PHP_EOL, PHP_EOL; - - try { - var_dump((new Randomizer(new $engine()))->shuffleBytes('foobar')); - } catch (Throwable $e) { - echo $e, PHP_EOL; - } - - echo PHP_EOL, "=====================", PHP_EOL; -} - -?> ---EXPECTF-- -===================== -EmptyStringEngine -===================== - -Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d -Stack trace: -#0 %s(%d): Random\Randomizer->getInt(0, 123) -#1 {main} - -------- - -Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d -Stack trace: -#0 %s(%d): Random\Randomizer->nextInt() -#1 {main} - -------- - -Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d -Stack trace: -#0 %s(%d): Random\Randomizer->getBytes(1) -#1 {main} - -------- - -Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d -Stack trace: -#0 %s(%d): Random\Randomizer->shuffleArray(Array) -#1 {main} - -------- - -Random\BrokenRandomEngineError: A random engine must return a non-empty string in %s:%d -Stack trace: -#0 %s(%d): Random\Randomizer->shuffleBytes('foobar') -#1 {main} - -===================== -HeavilyBiasedEngine -===================== - -Random\BrokenRandomEngineError: Failed to generate an acceptable random number in 50 attempts in %s:%d -Stack trace: -#0 %s(%d): Random\Randomizer->getInt(0, 123) -#1 {main} - -------- - -int(%d) - -------- - -string(2) "ff" - -------- - -Random\BrokenRandomEngineError: Failed to generate an acceptable random number in 50 attempts in %s:%d -Stack trace: -#0 %s(%d): Random\Randomizer->shuffleArray(Array) -#1 {main} - -------- - -Random\BrokenRandomEngineError: Failed to generate an acceptable random number in 50 attempts in %s:%d -Stack trace: -#0 %s(%d): Random\Randomizer->shuffleBytes('foobar') -#1 {main} - -=====================