feat: 기본 unit test 등록
This commit is contained in:
@@ -37,6 +37,9 @@ export class RandUtil {
|
|||||||
|
|
||||||
public shuffle<T>(srcArray: T[]): T[] {
|
public shuffle<T>(srcArray: T[]): T[] {
|
||||||
const cnt = srcArray.length;
|
const cnt = srcArray.length;
|
||||||
|
if(cnt === 0){
|
||||||
|
return [];
|
||||||
|
}
|
||||||
if (cnt > this.rng.getMaxInt()) {
|
if (cnt > this.rng.getMaxInt()) {
|
||||||
throw 'Invalid random int range';
|
throw 'Invalid random int range';
|
||||||
}
|
}
|
||||||
@@ -54,6 +57,9 @@ export class RandUtil {
|
|||||||
|
|
||||||
public choice<T>(items: T[] | Record<string | number, T> | Set<T>): T {
|
public choice<T>(items: T[] | Record<string | number, T> | Set<T>): T {
|
||||||
if (items instanceof Array) {
|
if (items instanceof Array) {
|
||||||
|
if(items.length === 0){
|
||||||
|
throw new Error('Empty items');
|
||||||
|
}
|
||||||
const idx = this.rng.nextInt(items.length - 1);
|
const idx = this.rng.nextInt(items.length - 1);
|
||||||
return items[idx];
|
return items[idx];
|
||||||
}
|
}
|
||||||
@@ -66,6 +72,9 @@ export class RandUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public choiceUsingWeight(items: Record<string | number, number>): string | number {
|
public choiceUsingWeight(items: Record<string | number, number>): string | number {
|
||||||
|
if(Object.keys(items).length === 0){
|
||||||
|
throw new Error('Empty items');
|
||||||
|
}
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
for (const value of Object.values(items)) {
|
for (const value of Object.values(items)) {
|
||||||
if (value <= 0) {
|
if (value <= 0) {
|
||||||
@@ -94,6 +103,9 @@ export class RandUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public choiceUsingWeightPair<T>(items: [T, number][]): T {
|
public choiceUsingWeightPair<T>(items: [T, number][]): T {
|
||||||
|
if(items.length === 0){
|
||||||
|
throw new Error('Empty items');
|
||||||
|
}
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
for (const [, value] of items) {
|
for (const [, value] of items) {
|
||||||
if (value <= 0) {
|
if (value <= 0) {
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ class RandUtil
|
|||||||
|
|
||||||
public function shuffle(array $srcArray): array
|
public function shuffle(array $srcArray): array
|
||||||
{
|
{
|
||||||
|
if(!$srcArray){
|
||||||
|
return $srcArray;
|
||||||
|
}
|
||||||
$cnt = count($srcArray);
|
$cnt = count($srcArray);
|
||||||
if ($cnt > $this->rng->getMaxInt()) {
|
if ($cnt > $this->rng->getMaxInt()) {
|
||||||
throw new \InvalidArgumentException("Invalid random int range");
|
throw new \InvalidArgumentException("Invalid random int range");
|
||||||
@@ -69,6 +72,9 @@ class RandUtil
|
|||||||
|
|
||||||
public function shuffleAssoc(array $srcArray): array
|
public function shuffleAssoc(array $srcArray): array
|
||||||
{
|
{
|
||||||
|
if(!$srcArray){
|
||||||
|
return $srcArray;
|
||||||
|
}
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ($this->shuffle(array_keys($srcArray)) as $key) {
|
foreach ($this->shuffle(array_keys($srcArray)) as $key) {
|
||||||
$result[$key] = $srcArray[$key];
|
$result[$key] = $srcArray[$key];
|
||||||
|
|||||||
@@ -0,0 +1,212 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use sammo\LiteHashDRBG;
|
||||||
|
use sammo\RandUtil;
|
||||||
|
|
||||||
|
const BLOCK_SIZE = LiteHashDRBG::BUFFER_BYTE_SIZE;
|
||||||
|
|
||||||
|
function fillBlock(string $src, string $filler = '\0', int $length = BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
$tmp = [$src];
|
||||||
|
$fillerLen = strlen($filler);
|
||||||
|
if ($fillerLen < 1) {
|
||||||
|
throw new \InvalidArgumentException('filler must have length');
|
||||||
|
}
|
||||||
|
|
||||||
|
$remainFill = intdiv($length - strlen($src), $fillerLen);
|
||||||
|
for (; $remainFill > 0; $remainFill--) {
|
||||||
|
$tmp[] = $filler;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = join("", $tmp);
|
||||||
|
|
||||||
|
$moreLen = $length - strlen($result);
|
||||||
|
if ($moreLen === 0) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result . substr($filler, 0, $moreLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DummyBlockRNG extends LiteHashDRBG
|
||||||
|
{
|
||||||
|
private int $repeatBlockCnt;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param string[] $repeatBlock
|
||||||
|
* @param int $stateIdx
|
||||||
|
* @return void
|
||||||
|
* @throws RuntimeException
|
||||||
|
*/
|
||||||
|
public function __construct(protected array $repeatBlock, protected int $stateIdx = 0)
|
||||||
|
{
|
||||||
|
foreach ($repeatBlock as $block) {
|
||||||
|
if (strlen($block) != BLOCK_SIZE) {
|
||||||
|
throw new RuntimeException('Invalid repeat block');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->repeatBlockCnt = count($this->repeatBlock);
|
||||||
|
$this->genNextBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function genNextBlock(): void
|
||||||
|
{
|
||||||
|
$this->buffer = $this->repeatBlock[$this->stateIdx];
|
||||||
|
$this->bufferIdx = 0;
|
||||||
|
$this->stateIdx = ($this->stateIdx + 1) % $this->repeatBlockCnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RNGTest extends PHPUnit\Framework\TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
const FIXED_KEY = 'HelloWorld';
|
||||||
|
public function testDummyRNG()
|
||||||
|
{
|
||||||
|
//Block의 값을 고정하고 입력값을 확인
|
||||||
|
$rng = new DummyBlockRNG([
|
||||||
|
fillBlock('', "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff")
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->setName('DummyRNG-SimpleByte');
|
||||||
|
$this->assertEquals("\x00", $rng->nextBytes(1));
|
||||||
|
$this->assertEquals("\x11\x22", $rng->nextBytes(2));
|
||||||
|
$this->assertEquals("\x33\x44\x55", $rng->nextBytes(3));
|
||||||
|
$this->assertEquals("\x66\x77\x88\x99", $rng->nextBytes(4));
|
||||||
|
|
||||||
|
$this->setName('DummyRNG-OverflowBlock');
|
||||||
|
foreach (range(1, 16) as $_i) {
|
||||||
|
$this->assertEquals(
|
||||||
|
"\xaa\xbb\xcc\xdd\xee\xff\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99",
|
||||||
|
$rng->nextBytes(16)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setName('DummyRNG-MultiBlock');
|
||||||
|
$this->assertEquals(
|
||||||
|
fillBlock('', "\xaa\xbb\xcc\xdd\xee\xff\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99", BLOCK_SIZE * 2),
|
||||||
|
$rng->nextBytes(BLOCK_SIZE * 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->setName('DummyRNG-BitTest');
|
||||||
|
$this->assertEquals("\x00", $rng->nextBits(1)); //aa
|
||||||
|
$this->assertEquals("\x01", $rng->nextBits(1)); //bb
|
||||||
|
$this->assertEquals("\xcc", $rng->nextBits(8)); //cc
|
||||||
|
$this->assertEquals("\xdd\x02", $rng->nextBits(10)); //ddee
|
||||||
|
$this->assertEquals("\x7f", $rng->nextBits(7)); //ff
|
||||||
|
$this->assertEquals("\x00\x11\x22\x33\x44\x55\x06", $rng->nextBits(53));
|
||||||
|
|
||||||
|
$this->setName('DummyRNG-Int');
|
||||||
|
$this->assertEquals(0x77, $rng->nextInt(0xff));
|
||||||
|
$this->assertEquals(0x9988, $rng->nextInt((1 << 16) - 1));
|
||||||
|
$this->assertEquals(0xddccbbaa, $rng->nextInt((1 << 32) - 1));
|
||||||
|
$this->assertEquals(0x0433221100ffee, $rng->nextInt());
|
||||||
|
$this->assertEquals(0x05, $rng->nextInt(0x0f)); //55
|
||||||
|
$this->assertEquals(0x06, $rng->nextInt(0x12)); //66
|
||||||
|
$this->assertEquals(0x08, $rng->nextInt(99)); //77(119 -> 7bit) -> 88(136 -> 8bit -> 8)
|
||||||
|
$this->assertEquals(0x99, $rng->nextInt(0x99)); //99
|
||||||
|
$this->assertEquals(0xaa, $rng->nextInt(0xaa)); //aa (fit Max)
|
||||||
|
|
||||||
|
$floatMax = 1 << 53;
|
||||||
|
$this->setName('DummyRNG-Float'); //7개씩
|
||||||
|
$fa = $rng->nextFloat1();
|
||||||
|
$this->assertEquals(0x1100ffeeddccbb / $floatMax, $fa, 'float1-1');
|
||||||
|
$this->assertIsFloat($fa);
|
||||||
|
$this->assertLessThan(0.5313720384, $fa);
|
||||||
|
$this->assertGreaterThan(0.5313720383, $fa);
|
||||||
|
|
||||||
|
$fb = $rng->nextFloat1();
|
||||||
|
$this->assertEquals(0x08776655443322 / $floatMax, $fb, 'float1-2');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRNGInvalidNextBits0()
|
||||||
|
{
|
||||||
|
$rng = new LiteHashDRBG(self::FIXED_KEY);
|
||||||
|
$this->expectException('\InvalidArgumentException');
|
||||||
|
$rng->nextBits(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRNGInvalidNextBitsMinus1()
|
||||||
|
{
|
||||||
|
$rng = new LiteHashDRBG(self::FIXED_KEY);
|
||||||
|
$this->expectException('\InvalidArgumentException');
|
||||||
|
$rng->nextBits(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRNGInvalidNextBytes0()
|
||||||
|
{
|
||||||
|
$rng = new LiteHashDRBG(self::FIXED_KEY);
|
||||||
|
$this->expectException('\InvalidArgumentException');
|
||||||
|
$rng->nextBytes(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRNGInvalidNextBytesMinus1()
|
||||||
|
{
|
||||||
|
$rng = new LiteHashDRBG(self::FIXED_KEY);
|
||||||
|
$this->expectException('\InvalidArgumentException');
|
||||||
|
$rng->nextBytes(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRNGInvalidNextIntOverflow()
|
||||||
|
{
|
||||||
|
$rng = new LiteHashDRBG(self::FIXED_KEY);
|
||||||
|
$this->expectException('\InvalidArgumentException');
|
||||||
|
$rng->nextInt(1 << 53);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRandUtilEmptyChoice()
|
||||||
|
{
|
||||||
|
$this->expectException('\InvalidArgumentException');
|
||||||
|
$rng = new LiteHashDRBG(self::FIXED_KEY);
|
||||||
|
$randUtil = new RandUtil($rng);
|
||||||
|
$randUtil->choice([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRandUtilEmptychoiceUsingWeight()
|
||||||
|
{
|
||||||
|
$this->expectException('\InvalidArgumentException');
|
||||||
|
$rng = new LiteHashDRBG(self::FIXED_KEY);
|
||||||
|
$randUtil = new RandUtil($rng);
|
||||||
|
$randUtil->choiceUsingWeight([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRandUtilEmptychoiceUsingWeightPair()
|
||||||
|
{
|
||||||
|
$this->expectException('\InvalidArgumentException');
|
||||||
|
$rng = new LiteHashDRBG(self::FIXED_KEY);
|
||||||
|
$randUtil = new RandUtil($rng);
|
||||||
|
$randUtil->choiceUsingWeightPair([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @doesNotPerformAssertions
|
||||||
|
*/
|
||||||
|
public function testRNGAcceptable()
|
||||||
|
{
|
||||||
|
$rng = new LiteHashDRBG(self::FIXED_KEY);
|
||||||
|
$rng->nextInt(0);
|
||||||
|
$rng->nextInt(1 << 53 - 1);
|
||||||
|
$rng->nextBytes(65);
|
||||||
|
$rng->nextBits(512);
|
||||||
|
|
||||||
|
$randUtil = new RandUtil($rng);
|
||||||
|
$randUtil->choice([0, 0, 0]);
|
||||||
|
$randUtil->choiceUsingWeight([0 => 0, 1 => -1]);
|
||||||
|
$randUtil->choiceUsingWeightPair([
|
||||||
|
[0, 0],
|
||||||
|
[1, 0],
|
||||||
|
[2, -1],
|
||||||
|
]);
|
||||||
|
$randUtil->nextBool(1.1);
|
||||||
|
$randUtil->nextBool(-0.1);
|
||||||
|
$randUtil->shuffle([]);
|
||||||
|
$randUtil->shuffle([1]);
|
||||||
|
$randUtil->shuffleAssoc([]);
|
||||||
|
$randUtil->shuffleAssoc([1]);
|
||||||
|
$randUtil->nextRange(0, 0);
|
||||||
|
$randUtil->nextRangeInt(0, 0);
|
||||||
|
$randUtil->nextRange(1, -1);
|
||||||
|
$randUtil->nextRangeInt(1, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user