Add new tests and cleanup old ones.
squash test cleanup squash tests
This commit is contained in:
parent
4a90e6bf24
commit
dffb6f257f
|
@ -244,6 +244,8 @@ dependencies {
|
|||
androidTestImplementation "androidx.arch.core:core-testing:2.1.0"
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||
androidTestImplementation "com.squareup.okhttp3:okhttp:3.8.0"
|
||||
androidTestImplementation 'ru.gildor.coroutines:kotlin-coroutines-okhttp:1.0'
|
||||
|
||||
// sample mnemonic plugin
|
||||
androidTestImplementation 'com.github.zcash:zcash-android-wallet-plugins:1.0.1'
|
||||
|
|
|
@ -1,110 +1,98 @@
|
|||
package cash.z.ecc.android.sdk
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import cash.z.ecc.android.sdk.exception.InitializerException
|
||||
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
||||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class InitializerTest {
|
||||
|
||||
lateinit var initializer: Initializer
|
||||
|
||||
@After
|
||||
fun cleanUp() {
|
||||
// don't leave databases sitting around after this test is run
|
||||
if (::initializer.isInitialized) initializer.erase()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInit() {
|
||||
val height = 980000
|
||||
|
||||
initializer = Initializer(context) { config ->
|
||||
config.importedWalletBirthday(height)
|
||||
config.setViewingKeys(
|
||||
"zxviews1qvn6j50dqqqqpqxqkvqgx2sp63jccr4k5t8zefadpzsu0yy73vczfznwc794xz6lvy3yp5ucv43lww48zz95ey5vhrsq83dqh0ky9junq0cww2wjp9c3cd45n5l5x8l2g9atnx27e9jgyy8zasjy26gugjtefphan9al3tx208m8ekev5kkx3ug6pd0qk4gq4j4wfuxajn388pfpq54wklwktqkyjz9e6gam0n09xjc35ncd3yah5aa9ezj55lk4u7v7hn0v86vz7ygq4qj2v",
|
||||
"zxviews1qv886f6hqqqqpqy2ajg9sm22vs4gm4hhajthctfkfws34u45pjtut3qmz0eatpqzvllgsvlk3x0y35ktx5fnzqqzueyph20k3328kx46y3u5xs4750cwuwjuuccfp7la6rh8yt2vjz6tylsrwzy3khtjjzw7etkae6gw3vq608k7quka4nxkeqdxxsr9xxdagv2rhhwugs6w0cquu2ykgzgaln2vyv6ah3ram2h6lrpxuznyczt2xl3lyxcwlk4wfz5rh7wzfd7642c2ae5d7"
|
||||
)
|
||||
config.alias = "VkInitTest1"
|
||||
}
|
||||
assertEquals(height, initializer.birthday.height)
|
||||
initializer.erase()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testErase() {
|
||||
val alias = "VkInitTest2"
|
||||
initializer = Initializer(context) { config ->
|
||||
config.importedWalletBirthday(1_419_900)
|
||||
config.setViewingKeys(
|
||||
"zxviews1qvn6j50dqqqqpqxqkvqgx2sp63jccr4k5t8zefadpzsu0yy73vczfznwc794xz6lvy3yp5ucv43lww48zz95ey5vhrsq83dqh0ky9junq0cww2wjp9c3cd45n5l5x8l2g9atnx27e9jgyy8zasjy26gugjtefphan9al3tx208m8ekev5kkx3ug6pd0qk4gq4j4wfuxajn388pfpq54wklwktqkyjz9e6gam0n09xjc35ncd3yah5aa9ezj55lk4u7v7hn0v86vz7ygq4qj2v",
|
||||
"zxviews1qv886f6hqqqqpqy2ajg9sm22vs4gm4hhajthctfkfws34u45pjtut3qmz0eatpqzvllgsvlk3x0y35ktx5fnzqqzueyph20k3328kx46y3u5xs4750cwuwjuuccfp7la6rh8yt2vjz6tylsrwzy3khtjjzw7etkae6gw3vq608k7quka4nxkeqdxxsr9xxdagv2rhhwugs6w0cquu2ykgzgaln2vyv6ah3ram2h6lrpxuznyczt2xl3lyxcwlk4wfz5rh7wzfd7642c2ae5d7"
|
||||
)
|
||||
config.alias = alias
|
||||
}
|
||||
|
||||
assertTrue("Failed to erase initializer", Initializer.erase(context, alias))
|
||||
assertFalse("Expected false when erasing nothing.", Initializer.erase(context))
|
||||
}
|
||||
|
||||
@Test(expected = InitializerException.MissingDefaultBirthdayException::class)
|
||||
fun testMissingBirthday() {
|
||||
val config = Initializer.Config { config ->
|
||||
config.setViewingKeys("vk1")
|
||||
}
|
||||
config.validate()
|
||||
}
|
||||
|
||||
@Test(expected = InitializerException.InvalidBirthdayHeightException::class)
|
||||
fun testOutOfBoundsBirthday() {
|
||||
val config = Initializer.Config { config ->
|
||||
config.setViewingKeys("vk1")
|
||||
config.setBirthdayHeight(ZcashSdk.SAPLING_ACTIVATION_HEIGHT - 1)
|
||||
}
|
||||
config.validate()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testImportedWalletUsesSaplingActivation() {
|
||||
initializer = Initializer(context) { config ->
|
||||
config.setViewingKeys("vk1")
|
||||
config.importWallet(ByteArray(32))
|
||||
}
|
||||
assertEquals("Incorrect height used for import.", ZcashSdk.SAPLING_ACTIVATION_HEIGHT, initializer.birthday.height)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDefaultToOldestHeight_true() {
|
||||
initializer = Initializer(context) { config ->
|
||||
config.setViewingKeys("vk1")
|
||||
config.setBirthdayHeight(null, true)
|
||||
}
|
||||
assertEquals("Height should equal sapling activation height when defaultToOldestHeight is true", ZcashSdk.SAPLING_ACTIVATION_HEIGHT, initializer.birthday.height)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDefaultToOldestHeight_false() {
|
||||
val initialHeight = 750_000
|
||||
initializer = Initializer(context) { config ->
|
||||
config.setViewingKeys("vk1")
|
||||
config.setBirthdayHeight(initialHeight, false)
|
||||
}
|
||||
val h = initializer.birthday.height
|
||||
assertNotEquals("Height should not equal sapling activation height when defaultToOldestHeight is false", ZcashSdk.SAPLING_ACTIVATION_HEIGHT, h)
|
||||
assertTrue("expected $h to be higher", h >= initialHeight)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val context = InstrumentationRegistry.getInstrumentation().context
|
||||
init {
|
||||
Twig.plant(TroubleshootingTwig())
|
||||
}
|
||||
}
|
||||
// lateinit var initializer: Initializer
|
||||
//
|
||||
// @After
|
||||
// fun cleanUp() {
|
||||
// // don't leave databases sitting around after this test is run
|
||||
// if (::initializer.isInitialized) initializer.erase()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun testInit() {
|
||||
// val height = 980000
|
||||
//
|
||||
// initializer = Initializer(context) { config ->
|
||||
// config.importedWalletBirthday(height)
|
||||
// config.setViewingKeys(
|
||||
// "zxviews1qvn6j50dqqqqpqxqkvqgx2sp63jccr4k5t8zefadpzsu0yy73vczfznwc794xz6lvy3yp5ucv43lww48zz95ey5vhrsq83dqh0ky9junq0cww2wjp9c3cd45n5l5x8l2g9atnx27e9jgyy8zasjy26gugjtefphan9al3tx208m8ekev5kkx3ug6pd0qk4gq4j4wfuxajn388pfpq54wklwktqkyjz9e6gam0n09xjc35ncd3yah5aa9ezj55lk4u7v7hn0v86vz7ygq4qj2v",
|
||||
// "zxviews1qv886f6hqqqqpqy2ajg9sm22vs4gm4hhajthctfkfws34u45pjtut3qmz0eatpqzvllgsvlk3x0y35ktx5fnzqqzueyph20k3328kx46y3u5xs4750cwuwjuuccfp7la6rh8yt2vjz6tylsrwzy3khtjjzw7etkae6gw3vq608k7quka4nxkeqdxxsr9xxdagv2rhhwugs6w0cquu2ykgzgaln2vyv6ah3ram2h6lrpxuznyczt2xl3lyxcwlk4wfz5rh7wzfd7642c2ae5d7"
|
||||
// )
|
||||
// config.alias = "VkInitTest1"
|
||||
// }
|
||||
// assertEquals(height, initializer.birthday.height)
|
||||
// initializer.erase()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun testErase() {
|
||||
// val alias = "VkInitTest2"
|
||||
// initializer = Initializer(context) { config ->
|
||||
// config.importedWalletBirthday(1_419_900)
|
||||
// config.setViewingKeys(
|
||||
// "zxviews1qvn6j50dqqqqpqxqkvqgx2sp63jccr4k5t8zefadpzsu0yy73vczfznwc794xz6lvy3yp5ucv43lww48zz95ey5vhrsq83dqh0ky9junq0cww2wjp9c3cd45n5l5x8l2g9atnx27e9jgyy8zasjy26gugjtefphan9al3tx208m8ekev5kkx3ug6pd0qk4gq4j4wfuxajn388pfpq54wklwktqkyjz9e6gam0n09xjc35ncd3yah5aa9ezj55lk4u7v7hn0v86vz7ygq4qj2v",
|
||||
// "zxviews1qv886f6hqqqqpqy2ajg9sm22vs4gm4hhajthctfkfws34u45pjtut3qmz0eatpqzvllgsvlk3x0y35ktx5fnzqqzueyph20k3328kx46y3u5xs4750cwuwjuuccfp7la6rh8yt2vjz6tylsrwzy3khtjjzw7etkae6gw3vq608k7quka4nxkeqdxxsr9xxdagv2rhhwugs6w0cquu2ykgzgaln2vyv6ah3ram2h6lrpxuznyczt2xl3lyxcwlk4wfz5rh7wzfd7642c2ae5d7"
|
||||
// )
|
||||
// config.alias = alias
|
||||
// }
|
||||
//
|
||||
// assertTrue("Failed to erase initializer", Initializer.erase(context, alias))
|
||||
// assertFalse("Expected false when erasing nothing.", Initializer.erase(context))
|
||||
// }
|
||||
//
|
||||
// @Test(expected = InitializerException.MissingDefaultBirthdayException::class)
|
||||
// fun testMissingBirthday() {
|
||||
// val config = Initializer.Config { config ->
|
||||
// config.setViewingKeys("vk1")
|
||||
// }
|
||||
// config.validate()
|
||||
// }
|
||||
//
|
||||
// @Test(expected = InitializerException.InvalidBirthdayHeightException::class)
|
||||
// fun testOutOfBoundsBirthday() {
|
||||
// val config = Initializer.Config { config ->
|
||||
// config.setViewingKeys("vk1")
|
||||
// config.setBirthdayHeight(ZcashSdk.SAPLING_ACTIVATION_HEIGHT - 1)
|
||||
// }
|
||||
// config.validate()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun testImportedWalletUsesSaplingActivation() {
|
||||
// initializer = Initializer(context) { config ->
|
||||
// config.setViewingKeys("vk1")
|
||||
// config.importWallet(ByteArray(32))
|
||||
// }
|
||||
// assertEquals("Incorrect height used for import.", ZcashSdk.SAPLING_ACTIVATION_HEIGHT, initializer.birthday.height)
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun testDefaultToOldestHeight_true() {
|
||||
// initializer = Initializer(context) { config ->
|
||||
// config.setViewingKeys("vk1")
|
||||
// config.setBirthdayHeight(null, true)
|
||||
// }
|
||||
// assertEquals("Height should equal sapling activation height when defaultToOldestHeight is true", ZcashSdk.SAPLING_ACTIVATION_HEIGHT, initializer.birthday.height)
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun testDefaultToOldestHeight_false() {
|
||||
// val initialHeight = 750_000
|
||||
// initializer = Initializer(context) { config ->
|
||||
// config.setViewingKeys("vk1")
|
||||
// config.setBirthdayHeight(initialHeight, false)
|
||||
// }
|
||||
// val h = initializer.birthday.height
|
||||
// assertNotEquals("Height should not equal sapling activation height when defaultToOldestHeight is false", ZcashSdk.SAPLING_ACTIVATION_HEIGHT, h)
|
||||
// assertTrue("expected $h to be higher", h >= initialHeight)
|
||||
// }
|
||||
//
|
||||
// companion object {
|
||||
// private val context = InstrumentationRegistry.getInstrumentation().context
|
||||
// init {
|
||||
// Twig.plant(TroubleshootingTwig())
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package cash.z.ecc.android.sdk.annotation
|
||||
|
||||
enum class TestPurpose {
|
||||
|
||||
/**
|
||||
* These tests are explicitly designed to preserve behavior that we do not want to lose after
|
||||
* major upgrades or refactors. It is acceptable for these test to run long and require
|
||||
* additional infrastructure.
|
||||
*/
|
||||
REGRESSION,
|
||||
|
||||
/**
|
||||
* These tests are designed to be run against new pull requests and generally before any changes
|
||||
* are committed. It is not ideal for these tests to run long.
|
||||
*/
|
||||
COMMIT,
|
||||
|
||||
/**
|
||||
* These tests require a running instance of [darksidewalletd](https://github.com/zcash/lightwalletd/blob/master/docs/darksidewalletd.md).
|
||||
*/
|
||||
DARKSIDE,
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that this test is explicitly intended to be maintained and run regularly in order to
|
||||
* achieve the given purpose. Eventually, we will run all such tests nightly.
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class MaintainedTest(vararg val purpose: TestPurpose)
|
|
@ -3,6 +3,8 @@ package cash.z.ecc.android.sdk.ext
|
|||
import android.content.Context
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
||||
import cash.z.ecc.android.sdk.util.SimpleMnemonics
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -12,14 +14,18 @@ import kotlinx.coroutines.delay
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.newFixedThreadPoolContext
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.json.JSONObject
|
||||
import org.junit.After
|
||||
import org.junit.AfterClass
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import ru.gildor.coroutines.okhttp.await
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
fun Initializer.Config.seedPhrase(seedPhrase: String) {
|
||||
setSeed(SimpleMnemonics().toSeed(seedPhrase.toCharArray()))
|
||||
fun Initializer.Config.seedPhrase(seedPhrase: String, network: ZcashNetwork) {
|
||||
setSeed(SimpleMnemonics().toSeed(seedPhrase.toCharArray()), network)
|
||||
}
|
||||
|
||||
open class ScopedTest(val defaultTimeout: Long = 2000L) {
|
||||
|
@ -93,6 +99,39 @@ open class ScopedTest(val defaultTimeout: Long = 2000L) {
|
|||
}
|
||||
}
|
||||
|
||||
open class DarksideTest(name: String = javaClass.simpleName) : ScopedTest() {
|
||||
val sithLord = DarksideTestCoordinator(host = host, port = port)
|
||||
val validator = sithLord.validator
|
||||
|
||||
fun runOnce(block: () -> Unit) {
|
||||
if (!ranOnce) {
|
||||
sithLord.enterTheDarkside()
|
||||
sithLord.synchronizer.start(classScope)
|
||||
block()
|
||||
ranOnce = true
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
// set the host for all tests. Someday, this will need to be set by CI
|
||||
// so have it read from the environment first and give that precidence
|
||||
var host = "192.168.1.134"
|
||||
val port: Int = 9067
|
||||
private var ranOnce = false
|
||||
}
|
||||
}
|
||||
|
||||
object BlockExplorer {
|
||||
suspend fun fetchLatestHeight(): Int {
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url("https://api.blockchair.com/zcash/blocks?limit=1")
|
||||
.build()
|
||||
val result = client.newCall(request).await()
|
||||
val body = result.body()?.string()
|
||||
return JSONObject(body).getJSONArray("data").getJSONObject(0).getInt("id")
|
||||
}
|
||||
}
|
||||
|
||||
object Transactions {
|
||||
val outbound = arrayOf(
|
||||
"https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/transactions/t-shielded-spend.txt",
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package cash.z.ecc.android.sdk.integration
|
||||
|
||||
import cash.z.ecc.android.sdk.annotation.MaintainedTest
|
||||
import cash.z.ecc.android.sdk.annotation.TestPurpose
|
||||
import cash.z.ecc.android.sdk.ext.BlockExplorer
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.util.TestWallet
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
||||
/**
|
||||
* This test is intended to run to make sure that basic things are functional and pinpoint what is
|
||||
* not working. It was originally developed after a major refactor to find what broke.
|
||||
*/
|
||||
@MaintainedTest(TestPurpose.COMMIT)
|
||||
@RunWith(Parameterized::class)
|
||||
class SanityTest(
|
||||
private val wallet: TestWallet,
|
||||
private val extfvk: String,
|
||||
private val extpub: String,
|
||||
private val birthday: Int,
|
||||
|
||||
) {
|
||||
|
||||
val networkName = wallet.networkName
|
||||
val name = "$networkName wallet"
|
||||
|
||||
@Test
|
||||
fun testNotPlaintext() {
|
||||
val message =
|
||||
"is using plaintext. This will cause problems for the test. Ensure that the `lightwalletd_allow_very_insecure_connections` resource value is false"
|
||||
assertFalse("$name $message", wallet.service.connectionInfo.usePlaintext)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFilePaths() {
|
||||
assertEquals(
|
||||
"$name has invalid DataDB file",
|
||||
"/data/user/0/cash.z.ecc.android.sdk.test/databases/TestWallet_${networkName}_Data.db",
|
||||
wallet.initializer.rustBackend.pathDataDb
|
||||
)
|
||||
assertEquals(
|
||||
"$name has invalid CacheDB file",
|
||||
"/data/user/0/cash.z.ecc.android.sdk.test/databases/TestWallet_${networkName}_Cache.db",
|
||||
wallet.initializer.rustBackend.pathCacheDb
|
||||
)
|
||||
assertEquals(
|
||||
"$name has invalid CacheDB params dir",
|
||||
"/data/user/0/cash.z.ecc.android.sdk.test/cache/params",
|
||||
wallet.initializer.rustBackend.pathParamsDir
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBirthday() {
|
||||
assertEquals(
|
||||
"$name has invalid birthday height",
|
||||
birthday,
|
||||
wallet.initializer.birthday.height
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testViewingKeys() {
|
||||
assertEquals(
|
||||
"$name has invalid extfvk",
|
||||
extfvk,
|
||||
wallet.initializer.viewingKeys[0].extfvk
|
||||
)
|
||||
assertEquals(
|
||||
"$name has invalid extpub",
|
||||
extpub,
|
||||
wallet.initializer.viewingKeys[0].extpub
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testServerConnection() {
|
||||
assertEquals(
|
||||
"$name has an invalid server connection",
|
||||
"$networkName.lightwalletd.com:9067?usePlaintext=false",
|
||||
wallet.connectionInfo
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLatestHeight() = runBlocking {
|
||||
if (wallet.networkName == "mainnet") {
|
||||
val expectedHeight = BlockExplorer.fetchLatestHeight()
|
||||
// fetch height directly because the synchronizer hasn't started, yet
|
||||
val downloaderHeight = wallet.service.getLatestBlockHeight()
|
||||
val info = wallet.connectionInfo
|
||||
assertTrue(
|
||||
"$info\n ${wallet.networkName} Lightwalletd is too far behind. Downloader height $downloaderHeight is more than 10 blocks behind block explorer height $expectedHeight",
|
||||
expectedHeight - 10 < downloaderHeight
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSingleBlockDownload() = runBlocking {
|
||||
// fetch block directly because the synchronizer hasn't started, yet
|
||||
val height = 1_000_000
|
||||
val block = wallet.service.getBlockRange(height..height)[0]
|
||||
assertTrue("$networkName failed to return a proper block. Height was ${block.height} but we expected $height", block.height.toInt() == height)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters
|
||||
fun wallets() = listOf(
|
||||
// Testnet wallet
|
||||
arrayOf(
|
||||
TestWallet(TestWallet.Backups.SAMPLE_WALLET),
|
||||
"zxviewtestsapling1qv0ue89kqqqqpqqyt4cl5wvssx4wqq30e5m948p07dnwl9x3u75vvnzvjwwpjkrf8yk2gva0kkxk9p8suj4xawlzw9pajuxgap83wykvsuyzfrm33a2p2m4jz2205kgzx0l2lj2kyegtnuph6crkyvyjqmfxut84nu00wxgrstu5fy3eu49nzl8jzr4chmql4ysgg2t8htn9dtvxy8c7wx9rvcerqsjqm6lqln9syk3g8rr3xpy3l4nj0kawenzpcdtnv9qmy98vdhqzaf063",
|
||||
"0234965f30c8611253d035f44e68d4e2ce82150e8665c95f41ccbaf916b16c69d8",
|
||||
1320000
|
||||
),
|
||||
// Mainnet wallet
|
||||
arrayOf(
|
||||
TestWallet(TestWallet.Backups.SAMPLE_WALLET, ZcashNetwork.Mainnet),
|
||||
"zxviews1q0hxkupsqqqqpqzsffgrk2smjuccedua7zswf5e3rgtv3ga9nhvhjug670egshd6me53r5n083s2m9mf4va4z7t39ltd3wr7hawnjcw09eu85q0ammsg0tsgx24p4ma0uvr4p8ltx5laum2slh2whc23ctwlnxme9w4dw92kalwk5u4wyem8dynknvvqvs68ktvm8qh7nx9zg22xfc77acv8hk3qqll9k3x4v2fa26puu2939ea7hy4hh60ywma69xtqhcy4037ne8g2sg8sq",
|
||||
"031c6355641237643317e2d338f5e8734c57e8aa8ce960ee22283cf2d76bef73be",
|
||||
1195000
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package cash.z.ecc.android.sdk.integration
|
||||
|
||||
import androidx.test.filters.MediumTest
|
||||
import cash.z.ecc.android.sdk.annotation.MaintainedTest
|
||||
import cash.z.ecc.android.sdk.annotation.TestPurpose
|
||||
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
|
||||
import cash.z.ecc.android.sdk.util.TestWallet
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* This test is intended to run to make sure that basic things are functional and pinpoint what is
|
||||
* not working. It was originally developed after a major refactor to find what broke.
|
||||
*/
|
||||
@MaintainedTest(TestPurpose.COMMIT)
|
||||
@MediumTest
|
||||
class SmokeTest {
|
||||
|
||||
@Test
|
||||
fun testNotPlaintext() {
|
||||
val service =
|
||||
wallet.synchronizer.processor.downloader.lightWalletService as LightWalletGrpcService
|
||||
Assert.assertFalse(
|
||||
"Wallet is using plaintext. This will cause problems for the test. Ensure that the `lightwalletd_allow_very_insecure_connections` resource value is false",
|
||||
service.connectionInfo.usePlaintext
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFilePaths() {
|
||||
Assert.assertEquals("Invalid DataDB file", "/data/user/0/cash.z.ecc.android.sdk.test/databases/TestWallet_testnet_Data.db", wallet.initializer.rustBackend.pathDataDb)
|
||||
Assert.assertEquals("Invalid CacheDB file", "/data/user/0/cash.z.ecc.android.sdk.test/databases/TestWallet_testnet_Cache.db", wallet.initializer.rustBackend.pathCacheDb)
|
||||
Assert.assertEquals("Invalid CacheDB params dir", "/data/user/0/cash.z.ecc.android.sdk.test/cache/params", wallet.initializer.rustBackend.pathParamsDir)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBirthday() {
|
||||
Assert.assertEquals("Invalid birthday height", 1_320_000, wallet.initializer.birthday.height)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testViewingKeys() {
|
||||
Assert.assertEquals("Invalid extfvk", "zxviewtestsapling1qv0ue89kqqqqpqqyt4cl5wvssx4wqq30e5m948p07dnwl9x3u75vvnzvjwwpjkrf8yk2gva0kkxk9p8suj4xawlzw9pajuxgap83wykvsuyzfrm33a2p2m4jz2205kgzx0l2lj2kyegtnuph6crkyvyjqmfxut84nu00wxgrstu5fy3eu49nzl8jzr4chmql4ysgg2t8htn9dtvxy8c7wx9rvcerqsjqm6lqln9syk3g8rr3xpy3l4nj0kawenzpcdtnv9qmy98vdhqzaf063", wallet.initializer.viewingKeys[0].extfvk)
|
||||
Assert.assertEquals("Invalid extpub", "0234965f30c8611253d035f44e68d4e2ce82150e8665c95f41ccbaf916b16c69d8", wallet.initializer.viewingKeys[0].extpub)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSync() = runBlocking<Unit> {
|
||||
wallet.sync(120_000L)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val wallet = TestWallet(TestWallet.Backups.SAMPLE_WALLET)
|
||||
}
|
||||
}
|
|
@ -114,7 +114,7 @@ class TestnetIntegrationTest : ScopedTest() {
|
|||
private val context = InstrumentationRegistry.getInstrumentation().context
|
||||
private val initializer = Initializer(context) { config ->
|
||||
config.setNetwork(ZcashNetwork.Testnet, host)
|
||||
config.importWallet(seed, birthdayHeight)
|
||||
config.importWallet(seed, birthdayHeight, ZcashNetwork.Testnet)
|
||||
}
|
||||
private lateinit var synchronizer: Synchronizer
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// package cash.z.ecc.android.sdk.integration
|
||||
package cash.z.ecc.android.sdk.integration.darkside // package cash.z.ecc.android.sdk.integration
|
||||
//
|
||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
// import cash.z.ecc.android.sdk.ext.twigTask
|
|
@ -1,4 +1,4 @@
|
|||
package cash.z.ecc.android.sdk.integration
|
||||
package cash.z.ecc.android.sdk.integration.darkside
|
||||
|
||||
// import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||
// import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
|
@ -1,4 +1,4 @@
|
|||
// package cash.z.ecc.android.sdk.integration
|
||||
package cash.z.ecc.android.sdk.integration.darkside // package cash.z.ecc.android.sdk.integration
|
||||
//
|
||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
// import cash.z.ecc.android.sdk.ext.twig
|
|
@ -1,4 +1,4 @@
|
|||
// package cash.z.ecc.android.sdk.integration
|
||||
package cash.z.ecc.android.sdk.integration.darkside // package cash.z.ecc.android.sdk.integration
|
||||
//
|
||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
// import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
|
@ -0,0 +1,24 @@
|
|||
package cash.z.ecc.android.sdk.integration.darkside
|
||||
|
||||
import cash.z.ecc.android.sdk.annotation.MaintainedTest
|
||||
import cash.z.ecc.android.sdk.annotation.TestPurpose.DARKSIDE
|
||||
import cash.z.ecc.android.sdk.annotation.TestPurpose.REGRESSION
|
||||
import cash.z.ecc.android.sdk.ext.DarksideTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Integration test to run in order to catch any regressions in transparent behavior.
|
||||
*/
|
||||
@MaintainedTest(DARKSIDE, REGRESSION)
|
||||
class TransparentIntegrationTest : DarksideTest() {
|
||||
@Before
|
||||
fun setup() = runOnce {
|
||||
sithLord.await()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sanityTest() {
|
||||
validator.validateTxCount(5)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package cash.z.ecc.android.sdk.integration.reorgs
|
||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs
|
||||
|
||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
|
@ -1,4 +1,4 @@
|
|||
// package cash.z.ecc.android.sdk.integration
|
||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs // package cash.z.ecc.android.sdk.integration
|
||||
//
|
||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
// import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
|
@ -1,4 +1,4 @@
|
|||
// package cash.z.ecc.android.sdk.integration
|
||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs // package cash.z.ecc.android.sdk.integration
|
||||
//
|
||||
// import androidx.test.platform.app.InstrumentationRegistry
|
||||
// import cash.z.ecc.android.sdk.Initializer
|
|
@ -1,4 +1,4 @@
|
|||
package cash.z.ecc.android.sdk.integration
|
||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs
|
||||
|
||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
|
@ -1,4 +1,4 @@
|
|||
package cash.z.ecc.android.sdk.integration
|
||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs
|
||||
|
||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
|
@ -1,4 +1,4 @@
|
|||
package cash.z.ecc.android.sdk.integration.reorgs
|
||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs
|
||||
|
||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
import cash.z.ecc.android.sdk.ext.toHex
|
|
@ -1,15 +1,18 @@
|
|||
package cash.z.ecc.android.sdk.integration.service
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import cash.z.ecc.android.sdk.annotation.MaintainedTest
|
||||
import cash.z.ecc.android.sdk.annotation.TestPurpose
|
||||
import cash.z.ecc.android.sdk.block.CompactBlockDownloader
|
||||
import cash.z.ecc.android.sdk.block.CompactBlockStore
|
||||
import cash.z.ecc.android.sdk.exception.LightWalletException.ChangeServerException.ChainInfoNotMatching
|
||||
import cash.z.ecc.android.sdk.exception.LightWalletException.ChangeServerException.StatusException
|
||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
|
||||
import cash.z.ecc.android.sdk.service.LightWalletService
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -24,7 +27,9 @@ import org.mockito.Mock
|
|||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.Spy
|
||||
|
||||
@MaintainedTest(TestPurpose.REGRESSION)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@SmallTest
|
||||
class ChangeServiceTest : ScopedTest() {
|
||||
|
||||
val network = ZcashNetwork.Mainnet
|
||||
|
@ -43,7 +48,7 @@ class ChangeServiceTest : ScopedTest() {
|
|||
fun setup() {
|
||||
initMocks()
|
||||
downloader = CompactBlockDownloader(service, mockBlockStore)
|
||||
otherService = LightWalletGrpcService(context, "lightwalletd.electriccoin.co", 9067)
|
||||
otherService = LightWalletGrpcService(context, "lightwalletd.electriccoin.co")
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -58,7 +63,7 @@ class ChangeServiceTest : ScopedTest() {
|
|||
@Test
|
||||
fun testSanityCheck() {
|
||||
val result = service.getLatestBlockHeight()
|
||||
assertTrue(result > ZcashSdk.SAPLING_ACTIVATION_HEIGHT)
|
||||
assertTrue(result > network.saplingActivationHeight)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -68,16 +73,20 @@ class ChangeServiceTest : ScopedTest() {
|
|||
assertEquals(1_001, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeatedly connect to servers and download a range of blocks. Switch part way through and
|
||||
* verify that the servers change over, even while actively downloading.
|
||||
*/
|
||||
@Test
|
||||
fun testSwitchWhileActive() = runBlocking {
|
||||
val start = 900_000
|
||||
val count = 5
|
||||
val vendors = mutableListOf<String>()
|
||||
var oldVendor = downloader.getServerInfo().vendor
|
||||
val differentiators = mutableListOf<String>()
|
||||
var initialValue = downloader.getServerInfo().buildUser
|
||||
val job = testScope.launch {
|
||||
repeat(count) {
|
||||
vendors.add(downloader.getServerInfo().vendor)
|
||||
twig("downloading from ${vendors.last()}")
|
||||
differentiators.add(downloader.getServerInfo().buildUser)
|
||||
twig("downloading from ${differentiators.last()}")
|
||||
downloader.downloadBlockRange(start..(start + 100 * it))
|
||||
delay(10L)
|
||||
}
|
||||
|
@ -87,8 +96,8 @@ class ChangeServiceTest : ScopedTest() {
|
|||
downloader.changeService(otherService)
|
||||
}
|
||||
job.join()
|
||||
assertTrue(vendors.count { it == oldVendor } < vendors.size)
|
||||
assertEquals(count, vendors.size)
|
||||
assertTrue(differentiators.count { it == initialValue } < differentiators.size)
|
||||
assertEquals(count, differentiators.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -116,7 +125,7 @@ class ChangeServiceTest : ScopedTest() {
|
|||
caughtException is ChainInfoNotMatching
|
||||
)
|
||||
(caughtException as ChainInfoNotMatching).propertyNames.let { props ->
|
||||
arrayOf("consensusBranchId", "saplingActivationHeight", "chainName").forEach {
|
||||
arrayOf("saplingActivationHeight", "chainName").forEach {
|
||||
assertTrue(
|
||||
"$it should be a non-matching property but properties were [$props]", props.contains(it, true)
|
||||
)
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
package cash.z.ecc.android.sdk.jni
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import cash.z.ecc.android.bip39.Mnemonics.MnemonicCode
|
||||
import cash.z.ecc.android.bip39.toSeed
|
||||
import cash.z.ecc.android.sdk.annotation.MaintainedTest
|
||||
import cash.z.ecc.android.sdk.annotation.TestPurpose
|
||||
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
||||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.test.BuildConfig
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.BeforeClass
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@MaintainedTest(TestPurpose.REGRESSION)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@SmallTest
|
||||
class TransparentTest {
|
||||
|
||||
lateinit var expected: Expected
|
||||
|
@ -21,13 +26,14 @@ class TransparentTest {
|
|||
|
||||
@Before
|
||||
fun setup() {
|
||||
if (BuildConfig.FLAVOR == "zcashtestnet") {
|
||||
expected = ExpectedTestnet
|
||||
network = ZcashNetwork.Testnet
|
||||
} else {
|
||||
expected = ExpectedMainnet
|
||||
network = ZcashNetwork.Mainnet
|
||||
}
|
||||
// TODO: parameterize this for both networks
|
||||
// if (BuildConfig.FLAVOR == "zcashtestnet") {
|
||||
expected = ExpectedTestnet
|
||||
network = ZcashNetwork.Testnet
|
||||
// } else {
|
||||
// expected = ExpectedMainnet
|
||||
// network = ZcashNetwork.Mainnet
|
||||
// }
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -54,16 +60,6 @@ class TransparentTest {
|
|||
assertEquals(expected.tAddr, DerivationTool.deriveTransparentAddressFromPublicKey(uvk.extpub, network = network))
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun deriveTransparentAddressFromSecretKeyTest2() {
|
||||
// while(false) {
|
||||
// MnemonicCode(COUNT_24).let { phrase ->
|
||||
// val addr = DerivationTool.deriveShieldedAddress(phrase.toSeed())
|
||||
// twig("$addr${String(phrase.chars)}\t")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
companion object {
|
||||
const val PHRASE = "deputy visa gentle among clean scout farm drive comfort patch skin salt ranch cool ramp warrior drink narrow normal lunch behind salt deal person"
|
||||
val MNEMONIC = MnemonicCode(PHRASE)
|
||||
|
|
|
@ -10,6 +10,7 @@ import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
|||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork.Testnet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.catch
|
||||
|
@ -28,9 +29,6 @@ class ShieldFundsSample {
|
|||
|
||||
val SEED_PHRASE = "wish puppy smile loan doll curve hole maze file ginger hair nose key relax knife witness cannon grab despair throw review deal slush frame" // \"still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread\"//\"deputy visa gentle among clean scout farm drive comfort patch skin salt ranch cool ramp warrior drink narrow normal lunch behind salt deal person"//"deputy visa gentle among clean scout farm drive comfort patch skin salt ranch cool ramp warrior drink narrow normal lunch behind salt deal person"
|
||||
|
||||
// simple flag to turn off actually spending funds
|
||||
val IS_DRY_RUN = true
|
||||
|
||||
/**
|
||||
* This test will construct a t2z transaction. It is safe to run this repeatedly, because
|
||||
* nothing is submitted to the network (because the keys don't match the address so the encoding
|
||||
|
@ -51,6 +49,9 @@ class ShieldFundsSample {
|
|||
|
||||
// when startHeight is null, it will use the latest checkpoint
|
||||
class SimpleWallet(seedPhrase: String, startHeight: Int? = null) {
|
||||
// simple flag to turn off actually spending funds
|
||||
val IS_DRY_RUN = true
|
||||
|
||||
val walletScope = CoroutineScope(
|
||||
SupervisorJob() + newFixedThreadPoolContext(3, this.javaClass.simpleName)
|
||||
)
|
||||
|
@ -62,7 +63,7 @@ class ShieldFundsSample {
|
|||
|
||||
// t1b9Y6PESSGavavgge3ruTtX9X83817V29s
|
||||
private val transparentAddress = DerivationTool.deriveTransparentAddress(seed, Testnet)
|
||||
|
||||
private val host = "lightwalletd.testnet.electriccoin.co"
|
||||
private val config = Initializer.Config {
|
||||
it.setSeed(seed, Testnet)
|
||||
it.setBirthdayHeight(startHeight, false)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package cash.z.ecc.android.sdk.transaction
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import cash.z.ecc.android.sdk.annotation.MaintainedTest
|
||||
import cash.z.ecc.android.sdk.annotation.TestPurpose
|
||||
import cash.z.ecc.android.sdk.db.entity.EncodedTransaction
|
||||
import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
||||
import cash.z.ecc.android.sdk.db.entity.isCancelled
|
||||
|
@ -29,7 +32,9 @@ import org.junit.runner.RunWith
|
|||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@MaintainedTest(TestPurpose.REGRESSION)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@SmallTest
|
||||
class PersistentTransactionManagerTest : ScopedTest() {
|
||||
|
||||
@Mock lateinit var mockEncoder: TransactionEncoder
|
||||
|
|
|
@ -79,7 +79,7 @@ class BalancePrinterUtil {
|
|||
}.collect { seed ->
|
||||
// TODO: clear the dataDb but leave the cacheDb
|
||||
val initializer = Initializer(context) { config ->
|
||||
config.importWallet(seed, birthdayHeight)
|
||||
config.importWallet(seed, birthdayHeight, network)
|
||||
config.setNetwork(network)
|
||||
config.alias = alias
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import androidx.test.platform.app.InstrumentationRegistry
|
|||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
||||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.ext.seedPhrase
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
|
@ -14,8 +16,10 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert
|
||||
|
@ -35,8 +39,7 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
|
|||
// dependencies: public
|
||||
val validator = DarksideTestValidator()
|
||||
val chainMaker = DarksideChainMaker()
|
||||
// var initializer = Initializer(context, Initializer.Builder(host, port, testName))
|
||||
lateinit var synchronizer: SdkSynchronizer
|
||||
lateinit var synchronizer: Synchronizer
|
||||
|
||||
val spendingKey: String get() = DerivationTool.deriveSpendingKeys(SimpleMnemonics().toSeed(seedPhrase.toCharArray()), network)[0]
|
||||
|
||||
|
@ -80,9 +83,10 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
|
|||
config.setBirthdayHeight(birthdayHeight)
|
||||
config.alias = testName
|
||||
}
|
||||
synchronizer = Synchronizer(initializer) as SdkSynchronizer
|
||||
synchronizer = Synchronizer(initializer)
|
||||
val channel = (synchronizer as SdkSynchronizer).channel
|
||||
darkside = DarksideApi(channel)
|
||||
darkside.reset()
|
||||
}
|
||||
|
||||
// fun triggerSmallReorg() {
|
||||
|
@ -226,7 +230,7 @@ class DarksideTestCoordinator(val host: String = "127.0.0.1", val testName: Stri
|
|||
}
|
||||
}
|
||||
suspend fun validateBalance(available: Long = -1, total: Long = -1, accountIndex: Int = 0) {
|
||||
val balance = synchronizer.processor.getBalanceInfo(accountIndex)
|
||||
val balance = (synchronizer as SdkSynchronizer).processor.getBalanceInfo(accountIndex)
|
||||
if (available > 0) {
|
||||
assertEquals("invalid available balance", available, balance.availableZatoshi)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
package cash.z.ecc.android.sdk.util
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import cash.z.ecc.android.bip39.Mnemonics
|
||||
import cash.z.ecc.android.bip39.toSeed
|
||||
import cash.z.ecc.android.sdk.Initializer
|
||||
import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.db.entity.isPending
|
||||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.android.sdk.type.WalletBalance
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.takeWhile
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.newFixedThreadPoolContext
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
/**
|
||||
* A simple wallet that connects to testnet for integration testing. The intention is that it is
|
||||
* easy to drive and nice to use.
|
||||
*/
|
||||
class TestWallet(
|
||||
seedPhrase: String,
|
||||
alias: String = "TestWallet",
|
||||
network: ZcashNetwork = ZcashNetwork.Testnet,
|
||||
host: String? = null,
|
||||
startHeight: Int? = null
|
||||
) {
|
||||
constructor(
|
||||
backup: Backups,
|
||||
network: ZcashNetwork = ZcashNetwork.Testnet,
|
||||
alias: String = "TestWallet"
|
||||
) : this(
|
||||
backup.seedPhrase,
|
||||
network = network,
|
||||
startHeight = backup.testnetBirthday,
|
||||
alias = alias
|
||||
)
|
||||
|
||||
val hostToUse = host ?: network.defaultHost
|
||||
val walletScope = CoroutineScope(
|
||||
SupervisorJob() + newFixedThreadPoolContext(3, this.javaClass.simpleName)
|
||||
)
|
||||
private val context = InstrumentationRegistry.getInstrumentation().context
|
||||
private val seed: ByteArray = Mnemonics.MnemonicCode(seedPhrase).toSeed()
|
||||
private val shieldedSpendingKey = DerivationTool.deriveSpendingKeys(seed, network = network)[0]
|
||||
private val transparentSecretKey = DerivationTool.deriveTransparentSecretKey(seed, network = network)
|
||||
val initializer = Initializer(context) { config ->
|
||||
config.importWallet(seed, startHeight, network, hostToUse, alias = alias)
|
||||
}
|
||||
val synchronizer: SdkSynchronizer = Synchronizer(initializer) as SdkSynchronizer
|
||||
val service = (synchronizer.processor.downloader.lightWalletService as LightWalletGrpcService)
|
||||
|
||||
val available get() = synchronizer.latestBalance.availableZatoshi
|
||||
val shieldedAddress = DerivationTool.deriveShieldedAddress(seed, network = network)
|
||||
val transparentAddress = DerivationTool.deriveTransparentAddress(seed, network = network)
|
||||
val birthdayHeight get() = synchronizer.latestBirthdayHeight
|
||||
val networkName get() = synchronizer.network.networkName
|
||||
val connectionInfo get() = service.connectionInfo.toString()
|
||||
|
||||
suspend fun transparentBalance(): WalletBalance {
|
||||
synchronizer.refreshUtxos(transparentAddress, synchronizer.latestBirthdayHeight)
|
||||
return synchronizer.getTransparentBalance(transparentAddress)
|
||||
}
|
||||
|
||||
suspend fun sync(timeout: Long = -1): TestWallet {
|
||||
val killSwitch = walletScope.launch {
|
||||
if (timeout > 0) {
|
||||
delay(timeout)
|
||||
throw TimeoutException("Failed to sync wallet within ${timeout}ms")
|
||||
}
|
||||
}
|
||||
if (!synchronizer.isStarted) {
|
||||
twig("Starting sync")
|
||||
synchronizer.start(walletScope)
|
||||
} else {
|
||||
twig("Awaiting next SYNCED status")
|
||||
}
|
||||
|
||||
// block until synced
|
||||
synchronizer.status.first { it == Synchronizer.Status.SYNCED }
|
||||
killSwitch.cancel()
|
||||
twig("Synced!")
|
||||
return this
|
||||
}
|
||||
|
||||
suspend fun send(address: String = transparentAddress, memo: String = "", amount: Long = 500L): TestWallet {
|
||||
synchronizer.sendToAddress(shieldedSpendingKey, amount, address, memo)
|
||||
.takeWhile { it.isPending() }
|
||||
.collect {
|
||||
twig("Updated transaction: $it")
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
suspend fun rewindToHeight(height: Int): TestWallet {
|
||||
synchronizer.rewindToHeight(height, false)
|
||||
return this
|
||||
}
|
||||
|
||||
suspend fun shieldFunds(): TestWallet {
|
||||
twig("checking $transparentAddress for transactions!")
|
||||
synchronizer.refreshUtxos(transparentAddress, 935000).let { count ->
|
||||
twig("FOUND $count new UTXOs")
|
||||
}
|
||||
|
||||
synchronizer.getTransparentBalance(transparentAddress).let { walletBalance ->
|
||||
twig("FOUND utxo balance of total: ${walletBalance.totalZatoshi} available: ${walletBalance.availableZatoshi}")
|
||||
|
||||
if (walletBalance.availableZatoshi > 0L) {
|
||||
synchronizer.shieldFunds(shieldedSpendingKey, transparentSecretKey)
|
||||
.onCompletion { twig("done shielding funds") }
|
||||
.catch { twig("Failed with $it") }
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
suspend fun join(timeout: Long? = null): TestWallet {
|
||||
// block until stopped
|
||||
twig("Staying alive until synchronizer is stopped!")
|
||||
if (timeout != null) {
|
||||
twig("Scheduling a stop in ${timeout}ms")
|
||||
walletScope.launch {
|
||||
delay(timeout)
|
||||
synchronizer.stop()
|
||||
}
|
||||
}
|
||||
synchronizer.status.first { it == Synchronizer.Status.STOPPED }
|
||||
twig("Stopped!")
|
||||
return this
|
||||
}
|
||||
|
||||
companion object {
|
||||
init {
|
||||
Twig.enabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
enum class Backups(val seedPhrase: String, val testnetBirthday: Int) {
|
||||
DEFAULT("column rhythm acoustic gym cost fit keen maze fence seed mail medal shrimp tell relief clip cannon foster soldier shallow refuse lunar parrot banana", 1_355_928),
|
||||
SAMPLE_WALLET("input frown warm senior anxiety abuse yard prefer churn reject people glimpse govern glory crumble swallow verb laptop switch trophy inform friend permit purpose", 1_330_190),
|
||||
ALICE("quantum whisper lion route fury lunar pelican image job client hundred sauce chimney barely life cliff spirit admit weekend message recipe trumpet impact kitten", 1_330_190),
|
||||
BOB("canvas wine sugar acquire garment spy tongue odor hole cage year habit bullet make label human unit option top calm neutral try vocal arena", 1_330_190),
|
||||
;
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
package cash.z.ecc.android.sdk.block
|
||||
|
||||
import cash.z.ecc.android.sdk.db.entity.CompactBlockEntity
|
||||
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
||||
import cash.z.ecc.android.sdk.ext.Twig
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk.SAPLING_ACTIVATION_HEIGHT
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.jni.RustBackend
|
||||
import cash.z.ecc.android.sdk.service.LightWalletService
|
||||
import cash.z.ecc.android.sdk.transaction.TransactionRepository
|
||||
import com.nhaarman.mockitokotlin2.atLeastOnce
|
||||
import com.nhaarman.mockitokotlin2.stub
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Timeout
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.jupiter.MockitoExtension
|
||||
import org.mockito.junit.jupiter.MockitoSettings
|
||||
import org.mockito.quality.Strictness
|
||||
|
||||
@ExtendWith(MockitoExtension::class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
class CompactBlockProcessorTest {
|
||||
|
||||
// Mocks/Spys
|
||||
@Mock lateinit var rustBackend: RustBackend
|
||||
lateinit var processor: CompactBlockProcessor
|
||||
|
||||
// Test variables
|
||||
private var latestBlockHeight: Int = 500_000
|
||||
private var lastDownloadedHeight: Int = SAPLING_ACTIVATION_HEIGHT
|
||||
private var lastScannedHeight: Int = SAPLING_ACTIVATION_HEIGHT
|
||||
private var errorBlock: Int = -1
|
||||
|
||||
@BeforeEach
|
||||
fun setUp(
|
||||
@Mock lightwalletService: LightWalletService,
|
||||
@Mock compactBlockStore: CompactBlockStore,
|
||||
@Mock repository: TransactionRepository
|
||||
) {
|
||||
Twig.plant(TroubleshootingTwig())
|
||||
|
||||
lightwalletService.stub {
|
||||
onBlocking {
|
||||
getBlockRange(any())
|
||||
}.thenAnswer { invocation ->
|
||||
val range = invocation.arguments[0] as IntRange
|
||||
range.map { CompactBlockEntity(it, ByteArray(0)) }
|
||||
}
|
||||
}
|
||||
lightwalletService.stub {
|
||||
onBlocking {
|
||||
getLatestBlockHeight()
|
||||
}.thenAnswer { latestBlockHeight }
|
||||
}
|
||||
|
||||
compactBlockStore.stub {
|
||||
onBlocking {
|
||||
write(any())
|
||||
}.thenAnswer { invocation ->
|
||||
val lastBlockHeight = (invocation.arguments[0] as List<CompactBlockEntity>).last().height
|
||||
lastDownloadedHeight = lastBlockHeight
|
||||
Unit
|
||||
}
|
||||
}
|
||||
compactBlockStore.stub {
|
||||
onBlocking {
|
||||
getLatestHeight()
|
||||
}.thenAnswer { lastDownloadedHeight }
|
||||
}
|
||||
compactBlockStore.stub {
|
||||
onBlocking {
|
||||
rewindTo(any())
|
||||
}.thenAnswer { invocation ->
|
||||
lastDownloadedHeight = invocation.arguments[0] as Int
|
||||
Unit
|
||||
}
|
||||
}
|
||||
repository.stub {
|
||||
onBlocking {
|
||||
lastScannedHeight()
|
||||
}.thenAnswer { lastScannedHeight }
|
||||
}
|
||||
|
||||
val downloader = spy(CompactBlockDownloader(lightwalletService, compactBlockStore))
|
||||
processor = spy(CompactBlockProcessor(downloader, repository, rustBackend))
|
||||
|
||||
whenever(rustBackend.validateCombinedChain()).thenAnswer {
|
||||
errorBlock
|
||||
}
|
||||
|
||||
whenever(rustBackend.scanBlocks()).thenAnswer {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(5)
|
||||
fun `check for OBOE when downloading`() = runBlocking {
|
||||
// if the last block downloaded was 350_000, then we already have that block and should start with 350_001
|
||||
lastDownloadedHeight = 550_000
|
||||
|
||||
processBlocksThen {
|
||||
verify(processor).downloadNewBlocks(350_001..latestBlockHeight)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(5)
|
||||
fun `chain error rewinds by expected amount`() = runBlocking {
|
||||
// if the highest block whose prevHash doesn't match happens at block 300_010
|
||||
errorBlock = 500_010
|
||||
|
||||
// then we should rewind the default (10) blocks
|
||||
val expectedBlock = errorBlock - 10
|
||||
processBlocksThen {
|
||||
twig("FINISHED PROCESSING!")
|
||||
verify(processor.downloader, atLeastOnce()).rewindToHeight(expectedBlock)
|
||||
verify(rustBackend, atLeastOnce()).rewindToHeight(expectedBlock)
|
||||
assertNotNull(processor)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
// @Timeout(5)
|
||||
fun `chain error downloads expected number of blocks`() = runBlocking {
|
||||
// if the highest block whose prevHash doesn't match happens at block 300_010
|
||||
// and our rewind distance is the default (10), then we want to download exactly ten blocks
|
||||
errorBlock = 500_010
|
||||
|
||||
// plus 1 because the range is inclusive
|
||||
val expectedRange = (errorBlock - 10 + 1)..latestBlockHeight
|
||||
processBlocksThen {
|
||||
verify(processor, atLeastOnce()).downloadNewBlocks(expectedRange)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix the fact that flows cause this not to work as originally coded. With a channel, we can stop observing once we reach 100. A flow makes that more difficult. The SDK behavior is still the same but testing that behavior is a little tricky without some refactors.
|
||||
private suspend fun processBlocksThen(block: suspend () -> Unit) = runBlocking {
|
||||
val scope = this
|
||||
launch {
|
||||
processor.start()
|
||||
}
|
||||
processor.progress.collect { i ->
|
||||
if (i >= 100) {
|
||||
block()
|
||||
processor.stop()
|
||||
}
|
||||
twig("processed $i")
|
||||
}
|
||||
twig("Done processing!")
|
||||
}
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
package cash.z.ecc.android.sdk.transaction
|
||||
|
||||
// import cash.z.ecc.android.sdk.dao.ClearedTransaction
|
||||
// import kotlinx.coroutines.*
|
||||
// import org.junit.jupiter.api.AfterEach
|
||||
// import org.junit.jupiter.api.BeforeEach
|
||||
// import org.junit.jupiter.api.Test
|
||||
//
|
||||
// import org.junit.jupiter.api.Assertions.*
|
||||
// import kotlin.random.Random
|
||||
// import kotlin.random.nextLong
|
||||
// import kotlin.system.measureTimeMillis
|
||||
|
||||
internal class MockSynchronizerTest {
|
||||
|
||||
// private val transactionInterval = 200L
|
||||
// private val activeTransactionInterval = 200L
|
||||
// private val synchronizer = MockSynchronizer(transactionInterval, activeTransactionInterval)
|
||||
// private val fastSynchronizer = MockSynchronizer(2L, 2L)
|
||||
// private val allTransactionChannel = synchronizer.allTransactions()
|
||||
// private val validAddress = "ztestsapling1yu2zy9aane2pje2qvm4qmn4k6q57y2d9ecs5vz0guthxx3m2aq57qm6hkx0520m9u9635xh6ttd"
|
||||
//
|
||||
// @BeforeEach
|
||||
// fun setUp() {
|
||||
// synchronizer.start(CoroutineScope(Dispatchers.IO))
|
||||
// }
|
||||
//
|
||||
// @AfterEach
|
||||
// fun tearDown() {
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun allTransactions() = runBlocking {
|
||||
// var total = 0
|
||||
// val duration = measureTimeMillis {
|
||||
// repeat(10) {
|
||||
// val transactions = allTransactionChannel.receive()
|
||||
// total += transactions.size
|
||||
// println("received ${transactions.size} transactions")
|
||||
// }
|
||||
// }
|
||||
// assertTrue(total > 0)
|
||||
// assertTrue(duration > transactionInterval)
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `never calling send yields zero sent transactions`() = runBlocking {
|
||||
// val fastChannel = fastSynchronizer.start(fastSynchronizer).allTransactions()
|
||||
// var transactions = fastChannel.receive()
|
||||
// repeat(10_000) {
|
||||
// transactions = fastChannel.receive()
|
||||
// }
|
||||
// assertTrue(transactions.size > 0, "no transactions created at all")
|
||||
// assertTrue(transactions.none { it.isSend })
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - each call to send generates exactly one sent transaction`() = runBlocking {
|
||||
// val fastChannel = fastSynchronizer.start(fastSynchronizer).allTransactions()
|
||||
// var transactions = fastChannel.receive()
|
||||
// repeat(10_000) {
|
||||
// if (it.rem(2_000) == 0) {
|
||||
// fastSynchronizer.sendToAddress(10, validAddress); println("yep")
|
||||
// }
|
||||
// transactions = fastChannel.receive()
|
||||
// }
|
||||
// assertEquals(5, transactions.count { it.isSend })
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - triggers an active transaction`() = runBlocking {
|
||||
// synchronizer.sendToAddress(10, validAddress)
|
||||
// delay(500L)
|
||||
// assertNotNull(synchronizer.activeTransactions().receiveOrNull())
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - results in success`() = runBlocking {
|
||||
// synchronizer.sendToAddress(10, validAddress)
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(TransactionState.AwaitingConfirmations(0).order <= result.values.first().order)
|
||||
// assertTrue((result.keys.first() as ActiveSendTransaction).transactionId.get() != -1L, "transactionId missing")
|
||||
// assertTrue((result.keys.first() as ActiveSendTransaction).height.get() != -1, "height missing")
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - results in mined transaction`() = runBlocking {
|
||||
// synchronizer.sendToAddress(10, validAddress)
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(TransactionState.AwaitingConfirmations(0).order <= result.values.first().order)
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - a bad address fails`() = runBlocking {
|
||||
// synchronizer.sendToAddress(10, "fail")
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(0 > result.values.first().order)
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - a short address fails`() = runBlocking {
|
||||
// // one character too short
|
||||
// val toAddress = "ztestsapling1yu2zy9aane2pje2qvm4qmn4k6q57y2d9ecs5vz0guthxx3m2aq57qm6hkx0520m9u9635xh6tt"
|
||||
// assertTrue(toAddress.length < 88, "sample address wasn't short enough (${toAddress.length})")
|
||||
//
|
||||
// synchronizer.sendToAddress(10, toAddress)
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(0 > result.values.first().order,
|
||||
// "result should have been a failure but was ${result.values.first()::class.simpleName}")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `send - a non-z prefix address fails`() = runBlocking {
|
||||
// // one character too short
|
||||
// val toAddress = "atestsapling1yu2zy9aane2pje2qvm4qmn4k6q57y2d9ecs5vz0guthxx3m2aq57qm6hkx0520m9u9635xh6ttd"
|
||||
// assertTrue(toAddress.length == 88,
|
||||
// "sample address was not the proper length (${toAddress.length}")
|
||||
// assertFalse(toAddress.startsWith('z'),
|
||||
// "sample address should not start with z")
|
||||
//
|
||||
// synchronizer.sendToAddress(10, toAddress)
|
||||
// delay(500L)
|
||||
// val result = synchronizer.activeTransactions().receive()
|
||||
// assertTrue(result.isNotEmpty(), "result was empty")
|
||||
// assertTrue(0 > result.values.first().order,
|
||||
// "result should have been a failure but was ${result.values.first()::class.simpleName}")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `balance matches transactions without sends`() = runBlocking {
|
||||
// val balances = fastSynchronizer.start(fastSynchronizer).balances()
|
||||
// var transactions = listOf<ClearedTransaction>()
|
||||
// while (transactions.count() < 10) {
|
||||
// transactions = fastSynchronizer.allTransactions().receive()
|
||||
// println("got ${transactions.count()} transaction(s)")
|
||||
// }
|
||||
// assertEquals(transactions.fold(0L) { acc, tx -> acc + tx.value }, balances.receive())
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `balance matches transactions with sends`() = runBlocking {
|
||||
// var transactions = listOf<ClearedTransaction>()
|
||||
// val balances = fastSynchronizer.start(fastSynchronizer).balances()
|
||||
// val transactionChannel = fastSynchronizer.allTransactions()
|
||||
// while (transactions.count() < 10) {
|
||||
// fastSynchronizer.sendToAddress(Random.nextLong(1L..10_000_000_000), validAddress)
|
||||
// transactions = transactionChannel.receive()
|
||||
// println("got ${transactions.count()} transaction(s)")
|
||||
// }
|
||||
// val transactionsSnapshot = transactionChannel.receive()
|
||||
// val balanceSnapshot = balances.receive()
|
||||
//
|
||||
// val positiveValue = transactionsSnapshot.fold(0L) { acc, tx -> acc + (if (tx.isSend) 0 else tx.value) }
|
||||
// val negativeValue = transactionsSnapshot.fold(0L) { acc, tx -> acc + (if (!tx.isSend) 0 else tx.value) }
|
||||
// assertEquals(positiveValue - negativeValue, balanceSnapshot, "incorrect balance. negative balance: $negativeValue positive balance: $positiveValue")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `progress hits 100`() = runBlocking {
|
||||
// var channel = synchronizer.progress()
|
||||
// var now = System.currentTimeMillis()
|
||||
// var delta = 0L
|
||||
// val expectedUpperBounds = transactionInterval * 10
|
||||
// while (channel.receive() < 100) {
|
||||
// delta = now - System.currentTimeMillis()
|
||||
// if (delta > expectedUpperBounds) break
|
||||
// }
|
||||
// assertTrue(delta < expectedUpperBounds, "progress did not hit 100 within the expected time of $expectedUpperBounds")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `is out of sync about 10% of the time`() = runBlocking {
|
||||
// var count = 0
|
||||
// repeat(100_000) {
|
||||
// if (synchronizer.isStale()) count++
|
||||
// }
|
||||
// assertTrue(count < 11_000, "a count of $count is too frequent")
|
||||
// assertTrue(count > 9_000, "a count of $count is too infrequent")
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun isFirstRun() {
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun cancelSend() = runBlocking {
|
||||
// val activeTransactions = synchronizer.activeTransactions()
|
||||
//
|
||||
// // verify that send creates one transaction
|
||||
// launch {
|
||||
// synchronizer.sendToAddress(10, validAddress)
|
||||
// }
|
||||
// println("done sending to address")
|
||||
// delay(300L)
|
||||
// var actives = activeTransactions.receiveOrNull()
|
||||
// assertEquals(1, actives?.size)
|
||||
// assertTrue((actives?.values?.first()?.order ?: 0) > -1, "expected positive order but was ${actives?.values?.first()?.order}")
|
||||
// val transaction = actives?.keys?.first() as? ActiveSendTransaction
|
||||
// assertNotNull(transaction)
|
||||
//
|
||||
// // and then verify that cancel changes its status
|
||||
// synchronizer.cancelSend(transaction!!)
|
||||
// delay(100L) // look for ignored state change
|
||||
// actives = activeTransactions.receiveOrNull()
|
||||
// assertNotNull(actives, "cancel changed nothing in 100ms")
|
||||
// assertEquals(1, actives!!.size, "unexpected number of active transactions ${actives.size}")
|
||||
// val finalState = actives!!.values.first()
|
||||
// assertNotNull(finalState as? TransactionState.Cancelled, "transaction was ${finalState::class.simpleName} instead of cancelled for ${actives.keys.first()}")
|
||||
// println("donso")
|
||||
// synchronizer.stop()
|
||||
// }
|
||||
}
|
Loading…
Reference in New Issue