mirrored from https://git.sr.ht/~ancarda/high-test-coverage
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement wrapper around random_bytes
- Loading branch information
Showing
14 changed files
with
397 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Ancarda\HighTestCoverage\RandomBytes; | ||
|
||
/** | ||
* Dispatch to a user function. | ||
* | ||
* This implementation calls the function given to the constructor every time | ||
* random bytes are requested. The user function is given the arguments | ||
* provided to randomBytes. This class is intended to be used when you need | ||
* arbitary or complex logic, but don't want to mock the RandomBytes interface. | ||
* | ||
* Please note that there are many implementations of RandomBytes including | ||
* Succession and OneShot that may implement the logic you are looking for. | ||
*/ | ||
final class Callback implements RandomBytes | ||
{ | ||
/** @var callable */ | ||
private $cb; | ||
|
||
public function __construct(callable $cb) | ||
{ | ||
$this->cb = $cb; | ||
} | ||
|
||
public function __invoke(int $length): string | ||
{ | ||
return call_user_func($this->cb, $length); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Ancarda\HighTestCoverage\RandomBytes; | ||
|
||
use RuntimeException; | ||
|
||
/** | ||
* Fail to generate random bytes every time | ||
* | ||
* This class always throws an exception when you request random bytes. It's | ||
* intended to be used to test how your code behaves when randomness is not | ||
* available. | ||
*/ | ||
final class Failure implements RandomBytes | ||
{ | ||
public function __invoke(int $length): string | ||
{ | ||
throw new RuntimeException('Could not gather sufficient random data'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Ancarda\HighTestCoverage\RandomBytes; | ||
|
||
/** | ||
* Return a predetermined fixed value every time | ||
* | ||
* This is the simplest possible implementation of RandomBytes. | ||
* The value given in the constructor is returned from invoke every time. | ||
*/ | ||
final class Fixed implements RandomBytes | ||
{ | ||
/** @var string */ | ||
private $value; | ||
|
||
public function __construct(string $value) | ||
{ | ||
$this->value = $value; | ||
} | ||
|
||
public function __invoke(int $length): string | ||
{ | ||
return $this->value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Ancarda\HighTestCoverage\RandomBytes; | ||
|
||
/** | ||
* Generate a single random string that is returned forever | ||
* | ||
* OneShot generates a single real random string, then returns that -- and | ||
* always that -- forever. | ||
* | ||
* This is intended to be used when you need uniformity across a test run, but | ||
* can have or want randomness between test runs. | ||
*/ | ||
final class OneShot implements RandomBytes | ||
{ | ||
/** @var string|null */ | ||
private $value = null; | ||
|
||
public function __invoke(int $length): string | ||
{ | ||
if ($this->value === null) { | ||
$this->value = random_bytes($length); | ||
} | ||
|
||
return $this->value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Ancarda\HighTestCoverage\RandomBytes; | ||
|
||
/** | ||
* Mockable wrapper around random_bytes | ||
* | ||
* You should typehint with this interface in all your code. A typical use | ||
* would be to have a constructor accept an instance like so: | ||
* | ||
* function __construct(RandomBytes $randomBytes) | ||
* | ||
* Which is then used throughout a class. Your Dependency Injection container | ||
* would then have an entry that resolves to Real: | ||
* | ||
* RandomBytes::class => Real::class, | ||
* | ||
* When that class is under test, you'll instead give it a class like Fixed. | ||
*/ | ||
interface RandomBytes | ||
{ | ||
public function __invoke(int $length): string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Ancarda\HighTestCoverage\RandomBytes; | ||
|
||
/** | ||
* Generate real random bytes | ||
* | ||
* This class just wraps random_bytes and is intended to be used in production | ||
* when you need a real random byte generator that you can mock. | ||
*/ | ||
final class Real implements RandomBytes | ||
{ | ||
public function __invoke(int $length): string | ||
{ | ||
return random_bytes($length); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Ancarda\HighTestCoverage\RandomBytes; | ||
|
||
use LogicException; | ||
|
||
/** | ||
* Return the next item in a set of predetermined fixed values every time | ||
* | ||
* This implementation takes a list of strings in the constructor. Each time | ||
* a random string is requested, the next item in the list is returned and | ||
* the pointer is moved one place. | ||
* | ||
* When the list is exhausted, the pointer wraps around. | ||
*/ | ||
final class Succession implements RandomBytes | ||
{ | ||
/** @var array<int, string> */ | ||
private $succession = []; | ||
|
||
/** @var int */ | ||
private $cursor = 0; | ||
|
||
/** @var int */ | ||
private $last = 0; | ||
|
||
/** | ||
* @param array<int, string> $succession Non-Empty array | ||
* @throws LogicException If given an empty array | ||
*/ | ||
public function __construct(array $succession) | ||
{ | ||
if (count($succession) === 0) { | ||
throw new LogicException('succession cannot be empty'); | ||
} | ||
|
||
$this->last = count($succession) - 1; | ||
$this->succession = $succession; | ||
} | ||
|
||
public function __invoke(int $length): string | ||
{ | ||
if ($this->cursor === $this->last) { | ||
$this->cursor = 0; | ||
return $this->succession[$this->last]; | ||
} | ||
|
||
return $this->succession[$this->cursor++]; | ||
} | ||
|
||
public function rewind(): void | ||
{ | ||
$this->cursor = 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\RandomBytes; | ||
|
||
use Ancarda\HighTestCoverage\RandomBytes\Callback; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
final class CallbackTest extends TestCase | ||
{ | ||
public function testCallback(): void | ||
{ | ||
$map = new Callback(function (int $code): string { | ||
if ($code === 1) { | ||
return 'Yes'; | ||
} | ||
|
||
return 'No'; | ||
}); | ||
|
||
self::assertSame('Yes', $map(1)); | ||
self::assertSame('No', $map(2)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\RandomBytes; | ||
|
||
use Ancarda\HighTestCoverage\RandomBytes\Failure; | ||
use PHPUnit\Framework\TestCase; | ||
use RuntimeException; | ||
|
||
final class FailureTest extends TestCase | ||
{ | ||
public function testThrowsException(): void | ||
{ | ||
$failure = new Failure(); | ||
|
||
$this->expectException(RuntimeException::class); | ||
|
||
$failure(6); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\RandomBytes; | ||
|
||
use Ancarda\HighTestCoverage\RandomBytes\Fixed; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
final class FixedTest extends TestCase | ||
{ | ||
public function testFixed(): void | ||
{ | ||
$fixed = new Fixed('abc'); | ||
|
||
$a = $fixed(3); | ||
$b = $fixed(3); | ||
|
||
self::assertSame($a, $b); | ||
} | ||
|
||
public function testReturnFixedValueOutsideRange(): void | ||
{ | ||
$fixed = new Fixed('abcdef'); | ||
|
||
self::assertSame('abcdef', $fixed(6)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\RandomBytes; | ||
|
||
use Ancarda\HighTestCoverage\RandomBytes\OneShot; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
final class OneShotTest extends TestCase | ||
{ | ||
public function testOneShotAlwaysReturnsSameString(): void | ||
{ | ||
$oneShot = new OneShot(); | ||
|
||
$output = $oneShot(10); | ||
self::assertSame($output, $oneShot(10)); | ||
} | ||
|
||
public function testLooksRandom(): void | ||
{ | ||
while (true) { | ||
$a = (new OneShot())(32); | ||
$b = (new OneShot())(32); | ||
|
||
if ($a !== $b) { | ||
self::assertNotSame($a, $b); | ||
return; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Tests\RandomBytes; | ||
|
||
use Ancarda\HighTestCoverage\RandomBytes\Real; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
final class RealTest extends TestCase | ||
{ | ||
public function testLooksRandom(): void | ||
{ | ||
$real = new Real(); | ||
|
||
while (true) { | ||
$c = $real(0xFF); | ||
$d = $real(0xFF); | ||
|
||
if ($c !== $d) { | ||
self::assertNotSame($c, $d); | ||
return; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.