core, contracts: Rewrite Generators to use functions instead of classes

This commit is contained in:
Andras Slemmer 2016-08-31 15:06:34 +01:00
parent 491b1abd4a
commit 2ac31a53b5
3 changed files with 91 additions and 115 deletions

View File

@ -16,73 +16,55 @@ import java.util.*
* This file contains generators for quickcheck style testing. The idea is that we can write random instance generators * This file contains generators for quickcheck style testing. The idea is that we can write random instance generators
* for each type we have in the code and test against those instead of predefined mock data. This style of testing can * for each type we have in the code and test against those instead of predefined mock data. This style of testing can
* catch corner case bugs and test algebraic properties of the code, for example deserialize(serialize(generatedThing)) == generatedThing * catch corner case bugs and test algebraic properties of the code, for example deserialize(serialize(generatedThing)) == generatedThing
*
* TODO add combinators for easier Generator writing
*/ */
class ContractStateGenerator : Generator<ContractState>(ContractState::class.java) { val contractStateGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState { Cash.State(
return Cash.State( amount = amountGenerator(issuedGenerator(currencyGenerator)).generate(random, status),
amount = AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status), owner = publicKeyGenerator.generate(random, status)
owner = PublicKeyGenerator().generate(random, status) )
)
}
} }
class MoveGenerator : Generator<Cash.Commands.Move>(Cash.Commands.Move::class.java) { val moveGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Cash.Commands.Move { Cash.Commands.Move(secureHashGenerator.generate(random, status))
return Cash.Commands.Move(SecureHashGenerator().generate(random, status))
}
} }
class IssueGenerator : Generator<Cash.Commands.Issue>(Cash.Commands.Issue::class.java) { val issueGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Cash.Commands.Issue { Cash.Commands.Issue(random.nextLong())
return Cash.Commands.Issue(random.nextLong())
}
} }
class ExitGenerator : Generator<Cash.Commands.Exit>(Cash.Commands.Exit::class.java) { val exitGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Cash.Commands.Exit { Cash.Commands.Exit(amountGenerator(issuedGenerator(currencyGenerator)).generate(random, status))
return Cash.Commands.Exit(AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status))
}
} }
class CommandDataGenerator : Generator<CommandData>(CommandData::class.java) { val commandDataGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): CommandData { val generators = listOf(moveGenerator, issueGenerator, exitGenerator)
val generators = listOf(MoveGenerator(), IssueGenerator(), ExitGenerator()) generators[random.nextInt(0, generators.size - 1)].generate(random, status)
return generators[random.nextInt(0, generators.size - 1)].generate(random, status)
}
} }
class CommandGenerator : Generator<Command>(Command::class.java) { val commandGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Command { val signersGenerator = ArrayListGenerator()
val signersGenerator = ArrayListGenerator() signersGenerator.addComponentGenerators(listOf(publicKeyGenerator))
signersGenerator.addComponentGenerators(listOf(PublicKeyGenerator())) Command(commandDataGenerator.generate(random, status), publicKeyGenerator.generate(random, status))
return Command(CommandDataGenerator().generate(random, status), PublicKeyGenerator().generate(random, status))
}
} }
class WiredTransactionGenerator: Generator<WireTransaction>(WireTransaction::class.java) { val wiredTransactionGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): WireTransaction { val commands = commandGenerator.generateList(random, status) + listOf(commandGenerator.generate(random, status))
val commands = CommandGenerator().generateList(random, status) + listOf(CommandGenerator().generate(random, status)) WireTransaction(
return WireTransaction( inputs = stateRefGenerator.generateList(random, status),
inputs = StateRefGenerator().generateList(random, status), attachments = secureHashGenerator.generateList(random, status),
attachments = SecureHashGenerator().generateList(random, status), outputs = transactionStateGenerator(contractStateGenerator).generateList(random, status),
outputs = TransactionStateGenerator(ContractStateGenerator()).generateList(random, status), commands = commands,
commands = commands, notary = partyGenerator.generate(random, status),
notary = PartyGenerator().generate(random, status), signers = commands.flatMap { it.signers },
signers = commands.flatMap { it.signers }, type = TransactionType.General(),
type = TransactionType.General(), timestamp = timestampGenerator.generate(random, status)
timestamp = TimestampGenerator().generate(random, status) )
)
}
} }
class SignedTransactionGenerator: Generator<SignedTransaction>(SignedTransaction::class.java) { val signedTransactionGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): SignedTransaction { val wireTransaction = wiredTransactionGenerator.generate(random, status)
val wireTransaction = WiredTransactionGenerator().generate(random, status) SignedTransaction(
return SignedTransaction( txBits = wireTransaction.serialized,
txBits = wireTransaction.serialized, sigs = listOf(NullSignature)
sigs = listOf(NullSignature) )
)
}
} }

View File

@ -3,6 +3,8 @@ package com.r3corda.core.testing
import com.pholser.junit.quickcheck.generator.GenerationStatus import com.pholser.junit.quickcheck.generator.GenerationStatus
import com.pholser.junit.quickcheck.generator.Generator import com.pholser.junit.quickcheck.generator.Generator
import com.pholser.junit.quickcheck.generator.java.lang.StringGenerator import com.pholser.junit.quickcheck.generator.java.lang.StringGenerator
import com.pholser.junit.quickcheck.generator.java.time.DurationGenerator
import com.pholser.junit.quickcheck.generator.java.time.InstantGenerator
import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator
import com.pholser.junit.quickcheck.random.SourceOfRandomness import com.pholser.junit.quickcheck.random.SourceOfRandomness
import com.r3corda.core.contracts.* import com.r3corda.core.contracts.*
@ -27,84 +29,71 @@ fun <A> Generator<A>.generateList(random: SourceOfRandomness, status: Generation
return arrayGenerator.generate(random, status) as List<A> return arrayGenerator.generate(random, status) as List<A>
} }
class PrivateKeyGenerator: Generator<PrivateKey>(PrivateKey::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): PrivateKey { inline fun <reified B : Any> generator(
return entropyToKeyPair(random.nextBigInteger(32)).private crossinline generatorFunction: (SourceOfRandomness, GenerationStatus) -> B
): Generator<B> {
return object : Generator<B>(B::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus) = generatorFunction(random, status)
} }
} }
class PublicKeyGenerator: Generator<PublicKey>(PublicKey::class.java) { val privateKeyGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): PublicKey { entropyToKeyPair(random.nextBigInteger(32)).private
return entropyToKeyPair(random.nextBigInteger(32)).public }
} class PrivateKeyGenerator : Generator<PrivateKey>(PrivateKey::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus) = privateKeyGenerator.generate(random, status)
} }
class PartyGenerator: Generator<Party>(Party::class.java) { val publicKeyGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Party { entropyToKeyPair(random.nextBigInteger(32)).public
return Party(StringGenerator().generate(random, status), PublicKeyGenerator().generate(random, status)) }
} class PublicKeyGenerator : Generator<PublicKey>(PublicKey::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus) = publicKeyGenerator.generate(random, status)
} }
class PartyAndReferenceGenerator: Generator<PartyAndReference>(PartyAndReference::class.java) { val partyGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): PartyAndReference { Party(StringGenerator().generate(random, status), publicKeyGenerator.generate(random, status))
return PartyAndReference(PartyGenerator().generate(random, status), OpaqueBytes(random.nextBytes(16)))
}
} }
class SecureHashGenerator: Generator<SecureHash>(SecureHash::class.java) { val partyAndReferenceGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): SecureHash { PartyAndReference(partyGenerator.generate(random, status), OpaqueBytes(random.nextBytes(16)))
return SecureHash.Companion.sha256(random.nextBytes(16))
}
} }
class StateRefGenerator: Generator<StateRef>(StateRef::class.java) { val secureHashGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): StateRef { SecureHash.Companion.sha256(random.nextBytes(16))
return StateRef(SecureHash.Companion.sha256(random.nextBytes(16)), random.nextInt(0, 10))
}
} }
class TransactionStateGenerator<T : ContractState>(val stateGenerator: Generator<T>) : Generator<TransactionState<T>>(TransactionState::class.java as Class<TransactionState<T>>) { val stateRefGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): TransactionState<T> { StateRef(SecureHash.Companion.sha256(random.nextBytes(16)), random.nextInt(0, 10))
return TransactionState(stateGenerator.generate(random, status), PartyGenerator().generate(random, status))
}
} }
class IssuedGenerator<T>(val productGenerator: Generator<T>) : Generator<Issued<T>>(Issued::class.java as Class<Issued<T>>) { fun <T : ContractState> transactionStateGenerator(stateGenerator: Generator<T>) = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Issued<T> { TransactionState(stateGenerator.generate(random, status), partyGenerator.generate(random, status))
return Issued(PartyAndReferenceGenerator().generate(random, status), productGenerator.generate(random, status))
}
} }
class AmountGenerator<T>(val tokenGenerator: Generator<T>) : Generator<Amount<T>>(Amount::class.java as Class<Amount<T>>) { fun <T> issuedGenerator(productGenerator: Generator<T>) = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Amount<T> { Issued(partyAndReferenceGenerator.generate(random, status), productGenerator.generate(random, status))
return Amount(random.nextLong(0, 1000000), tokenGenerator.generate(random, status))
}
} }
class CurrencyGenerator() : Generator<Currency>(Currency::class.java) { fun <T> amountGenerator(tokenGenerator: Generator<T>) = generator { random, status ->
companion object { Amount(random.nextLong(0, 1000000), tokenGenerator.generate(random, status))
val currencies = Currency.getAvailableCurrencies().toList()
}
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Currency {
return currencies[random.nextInt(0, currencies.size - 1)]
}
} }
class InstantGenerator : Generator<Instant>(Instant::class.java) { private val currencies = Currency.getAvailableCurrencies().toList()
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Instant { val currencyGenerator = generator { random, status ->
return Instant.ofEpochMilli(random.nextLong(0, 1000000)) currencies[random.nextInt(0, currencies.size - 1)]
}
} }
class DurationGenerator : Generator<Duration>(Duration::class.java) { val instantGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Duration { Instant.ofEpochMilli(random.nextLong(0, 1000000))
return Duration.ofMillis(random.nextLong(0, 1000000))
}
} }
class TimestampGenerator : Generator<Timestamp>(Timestamp::class.java) { val durationGenerator = generator { random, status ->
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Timestamp { Duration.ofMillis(random.nextLong(0, 1000000))
return Timestamp(InstantGenerator().generate(random, status), DurationGenerator().generate(random, status)) }
}
val timestampGenerator = generator { random, status ->
Timestamp(InstantGenerator().generate(random, status), DurationGenerator().generate(random, status))
} }

View File

@ -2,15 +2,17 @@ package com.r3corda.core.protocols
import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Input
import com.pholser.junit.quickcheck.From import com.pholser.junit.quickcheck.From
import com.pholser.junit.quickcheck.Produced
import com.pholser.junit.quickcheck.Property import com.pholser.junit.quickcheck.Property
import com.pholser.junit.quickcheck.generator.GenerationStatus import com.pholser.junit.quickcheck.generator.GenerationStatus
import com.pholser.junit.quickcheck.generator.Generator import com.pholser.junit.quickcheck.generator.Generator
import com.pholser.junit.quickcheck.random.SourceOfRandomness import com.pholser.junit.quickcheck.random.SourceOfRandomness
import com.pholser.junit.quickcheck.runner.JUnitQuickcheck import com.pholser.junit.quickcheck.runner.JUnitQuickcheck
import com.r3corda.contracts.testing.SignedTransactionGenerator import com.r3corda.contracts.testing.signedTransactionGenerator
import com.r3corda.core.serialization.createKryo import com.r3corda.core.serialization.createKryo
import com.r3corda.core.serialization.serialize import com.r3corda.core.serialization.serialize
import com.r3corda.core.testing.PartyGenerator import com.r3corda.core.testing.generator
import com.r3corda.core.testing.partyGenerator
import com.r3corda.protocols.BroadcastTransactionProtocol import com.r3corda.protocols.BroadcastTransactionProtocol
import org.junit.runner.RunWith import org.junit.runner.RunWith
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -18,15 +20,18 @@ import kotlin.test.assertEquals
@RunWith(JUnitQuickcheck::class) @RunWith(JUnitQuickcheck::class)
class BroadcastTransactionProtocolTest { class BroadcastTransactionProtocolTest {
class NotifyTxRequestMessageGenerator : Generator<BroadcastTransactionProtocol.NotifyTxRequestMessage>(BroadcastTransactionProtocol.NotifyTxRequestMessage::class.java) { companion object {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): BroadcastTransactionProtocol.NotifyTxRequestMessage { val notifyTxRequestMessageGenerator = generator { random, status ->
return BroadcastTransactionProtocol.NotifyTxRequestMessage( BroadcastTransactionProtocol.NotifyTxRequestMessage(
tx = SignedTransactionGenerator().generate(random, status), tx = signedTransactionGenerator.generate(random, status),
events = setOf(), events = setOf(),
replyToParty = PartyGenerator().generate(random, status), replyToParty = partyGenerator.generate(random, status),
sessionID = random.nextLong() sessionID = random.nextLong()
) )
} }
class NotifyTxRequestMessageGenerator: Generator<BroadcastTransactionProtocol.NotifyTxRequestMessage>(BroadcastTransactionProtocol.NotifyTxRequestMessage::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus) = notifyTxRequestMessageGenerator.generate(random, status)
}
} }
@Property @Property