123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- <?php
- /*
- * The RandomLib library for securely generating random numbers and strings in PHP
- *
- * @author Anthony Ferrara <ircmaxell@ircmaxell.com>
- * @copyright 2011 The Authors
- * @license http://www.opensource.org/licenses/mit-license.html MIT License
- * @version Build @@version@@
- */
- use RandomLib\Generator;
- use RandomLibTest\Mocks\Random\Mixer;
- use RandomLibTest\Mocks\Random\Source;
- class Vectors_Random_GeneratorTest extends PHPUnit_Framework_TestCase
- {
- public static function provideGenerateInt()
- {
- return array(
- // First, lets test each offset based range
- array(0, 7),
- array(0, 15),
- array(0, 31),
- array(0, 63),
- array(0, 127),
- array(0, 255),
- array(0, 511),
- array(0, 1023),
- // Let's try a range not starting at 0
- array(8, 15),
- // Let's try a range with a negative number
- array(-18, -11),
- // Let's try a non-power-of-2 range
- array(10, 100),
- // Finally, let's try two large numbers
- array(100000, 100007),
- array(100000000, 100002047),
- // Now, let's force a few loops by setting a valid offset
- array(0, 5, 2),
- array(0, 9, 5),
- array(0, 27, 4),
- );
- }
- public static function provideGenerators()
- {
- $factory = new \RandomLib\Factory();
- $generator = $factory->getLowStrengthGenerator();
- $sources = $generator->getSources();
- $ret = array();
- $ret[] = array(new Generator($sources, new \RandomLib\Mixer\Hash()), 10000, 'hash');
- return $ret;
- }
- /**
- * This test asserts that the algorithm that generates the integers does not
- * actually introduce any bias into the generated numbers. If this test
- * passes, the generated integers from the generator will be as unbiased as
- * the sources that provide the data.
- *
- * @dataProvider provideGenerateInt
- */
- public function testGenerateInt($min, $max, $offset = 0)
- {
- $generator = $this->getGenerator($max - $min + $offset);
- for ($i = $max; $i >= $min; $i--) {
- $this->assertEquals($i, $generator->generateInt($min, $max));
- }
- }
- /**
- * This generator generates two bytes at a time, and uses each 8 bit segment of
- * the generated byte as a coordinate on a grid (so 01011010 would be the
- * coordinate (0101, 1010) or (5, 10). These are used as inputs to a MonteCarlo
- * algorithm for the integral of y=x over a 15x15 grid. The expected answer is
- * 1/2 * 15 * 15 (or 1/2 * base * height, since the result is a triangle).
- * Therefore, if we get an answer close to that, we know the generator is good.
- *
- * Now, since the area under the line should be equal to the area above the line.
- * Therefore, the ratio of the two areas should be equal. This way, we can avoid
- * computing total to figure out the areas.
- *
- * I have set the bounds on the test to be 80% and 120%. Meaning that I will
- * consider the test valid and unbiased if the number of random elements that
- * fall under (inside) of the line and the number that fall outside of the line
- * are at most 20% apart.
- *
- * Since testing randomness is not reliable or repeatable, I will only fail the
- * test in two different scenarios. The first is if after the iterations the
- * outside or the inside is 0. The chances of that happening are so low that
- * if it happens, it's relatively safe to assume that something bad happened. The
- * second scenario happens when the ratio is outside of the 20% tolerance. If
- * that happens, I will re-run the entire test. If that test is outside of the 20%
- * tolerance, then the test will fail
- *
- *
- * @dataProvider provideGenerators
- */
- public function testGenerate(\RandomLib\Generator $generator, $times)
- {
- $ratio = $this->doTestGenerate($generator, $times);
- if ($ratio < 0.8 || $ratio > 1.2) {
- $ratio2 = $this->doTestGenerate($generator, $times);
- if ($ratio2 > 1.2 || $ratio2 < 0.8) {
- $this->fail(
- sprintf(
- 'The test failed multiple runs with final ratios %f and %f',
- $ratio,
- $ratio2
- )
- );
- }
- }
- }
- protected function doTestGenerate(\RandomLib\Generator $generator, $times)
- {
- $inside = 0;
- $outside = 0;
- $on = 0;
- for ($i = 0; $i < $times; $i++) {
- $byte = $generator->generate(2);
- $byte = unpack('n', $byte);
- $byte = array_shift($byte);
- $xCoord = ($byte >> 8);
- $yCoord = ($byte & 0xFF);
- if ($xCoord < $yCoord) {
- $outside++;
- } elseif ($xCoord == $yCoord) {
- $on++;
- } else {
- $inside++;
- }
- }
- $this->assertGreaterThan(0, $outside, 'Outside Is 0');
- $this->assertGreaterThan(0, $inside, 'Inside Is 0');
- $ratio = $inside / $outside;
- return $ratio;
- }
- public function getGenerator($random)
- {
- $source1 = new Source(array(
- 'generate' => function ($size) use (&$random) {
- $ret = pack('N', $random);
- $random--;
- return substr($ret, -1 * $size);
- },
- ));
- $sources = array($source1);
- $mixer = new Mixer(array(
- 'mix'=> function (array $sources) {
- if (empty($sources)) {
- return '';
- }
- return array_pop($sources);
- },
- ));
- return new Generator($sources, $mixer);
- }
- }
|