diceware: add Die class and tests
This commit is contained in:
parent
67f8eb8404
commit
16392b7581
4 changed files with 102 additions and 0 deletions
10
passgen/diceware/api/diceware.api
Normal file
10
passgen/diceware/api/diceware.api
Normal file
|
@ -0,0 +1,10 @@
|
|||
public final class dev/msfjarvis/aps/passgen/diceware/Die {
|
||||
public fun <init> (ILdev/msfjarvis/aps/passgen/diceware/RandomIntGenerator;)V
|
||||
public final fun roll ()I
|
||||
public final fun rollMultiple (I)I
|
||||
}
|
||||
|
||||
public abstract interface class dev/msfjarvis/aps/passgen/diceware/RandomIntGenerator {
|
||||
public abstract fun get (Lkotlin/ranges/IntRange;)I
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.passgen.diceware
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
/** Basic implementation of a die with configurable number of sides. */
|
||||
public class Die
|
||||
@Inject
|
||||
constructor(
|
||||
private val sides: Int,
|
||||
private val random: RandomIntGenerator,
|
||||
) {
|
||||
|
||||
/** Roll the die to return a single number. */
|
||||
public fun roll(): Int {
|
||||
return random.get(1..sides)
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll the die multiple times, concatenating each result to obtain a number with [iterations]
|
||||
* digits.
|
||||
*/
|
||||
public fun rollMultiple(iterations: Int): Int {
|
||||
return StringBuilder().apply { repeat(iterations) { append(roll()) } }.toString().toInt()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.passgen.diceware
|
||||
|
||||
/**
|
||||
* SAM interface that takes in an [IntRange] and returns a randomly chosen [Int] within its bounds.
|
||||
* This is used as a replacement for [kotlin.random.Random] since there is no CSPRNG-backed
|
||||
* implementation of it in the Kotlin stdlib.
|
||||
*/
|
||||
public fun interface RandomIntGenerator {
|
||||
public fun get(range: IntRange): Int
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
package dev.msfjarvis.aps.passgen.diceware
|
||||
|
||||
import kotlin.random.Random
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class DieTest {
|
||||
|
||||
/** Pre-seeded [Random] instance to ensure tests are deterministic. */
|
||||
private val random = Random(1_00_000)
|
||||
|
||||
private val intGenerator = RandomIntGenerator { it.random(random) }
|
||||
|
||||
@Test
|
||||
fun test_one_roll() {
|
||||
val die = Die(6, intGenerator)
|
||||
assertEquals(5, die.roll())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_multiple_rolls() {
|
||||
val die = Die(6, intGenerator)
|
||||
assertEquals(526242, die.rollMultiple(6))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_consecutive_rolls() {
|
||||
val die = Die(6, intGenerator)
|
||||
assertEquals(5, die.roll())
|
||||
assertEquals(2, die.roll())
|
||||
assertEquals(6, die.roll())
|
||||
assertEquals(2, die.roll())
|
||||
assertEquals(4, die.roll())
|
||||
assertEquals(2, die.roll())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_100_sides() {
|
||||
val die = Die(100, intGenerator)
|
||||
assertEquals(67, die.roll())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue