Merge pull request #287 from zcash/284-darkside-test-module
Refactor darkside tests to separate module
This commit is contained in:
commit
fde9257207
|
@ -0,0 +1,53 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name=":darkside-test-lib:connectedAndroidTest" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
|
||||||
|
<module name="zcash-android-sdk.darkside-test-lib" />
|
||||||
|
<option name="TESTING_TYPE" value="0" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="CLASS_NAME" value="" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
|
||||||
|
<option name="EXTRA_OPTIONS" value="" />
|
||||||
|
<option name="INCLUDE_GRADLE_EXTRA_OPTIONS" value="true" />
|
||||||
|
<option name="CLEAR_LOGCAT" value="false" />
|
||||||
|
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||||
|
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
|
||||||
|
<option name="FORCE_STOP_RUNNING_APP" value="true" />
|
||||||
|
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||||
|
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="2147483645" />
|
||||||
|
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="api-9130115880275692386-873230" />
|
||||||
|
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||||
|
<Auto>
|
||||||
|
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||||
|
<option name="SHOW_STATIC_VARS" value="true" />
|
||||||
|
<option name="WORKING_DIR" value="" />
|
||||||
|
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||||
|
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||||
|
</Auto>
|
||||||
|
<Hybrid>
|
||||||
|
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||||
|
<option name="SHOW_STATIC_VARS" value="true" />
|
||||||
|
<option name="WORKING_DIR" value="" />
|
||||||
|
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||||
|
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||||
|
</Hybrid>
|
||||||
|
<Java />
|
||||||
|
<Native>
|
||||||
|
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||||
|
<option name="SHOW_STATIC_VARS" value="true" />
|
||||||
|
<option name="WORKING_DIR" value="" />
|
||||||
|
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||||
|
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||||
|
</Native>
|
||||||
|
<Profilers>
|
||||||
|
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
|
||||||
|
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||||
|
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||||
|
</Profilers>
|
||||||
|
<method v="2">
|
||||||
|
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
|
@ -0,0 +1,33 @@
|
||||||
|
plugins {
|
||||||
|
id("com.android.library")
|
||||||
|
id("zcash.android-build-conventions")
|
||||||
|
id("kotlin-android")
|
||||||
|
id("kotlin-kapt")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
defaultConfig {
|
||||||
|
//targetSdk = 30 //Integer.parseInt(project.property("targetSdkVersion"))
|
||||||
|
multiDexEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to figure out how to move this into the build-conventions
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = libs.versions.java.get()
|
||||||
|
allWarningsAsErrors = project.property("IS_TREAT_WARNINGS_AS_ERRORS").toString().toBoolean()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(projects.sdkLib)
|
||||||
|
implementation(libs.kotlin.stdlib)
|
||||||
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
implementation(libs.kotlinx.coroutines.android)
|
||||||
|
implementation(libs.androidx.multidex)
|
||||||
|
implementation(libs.bundles.grpc)
|
||||||
|
|
||||||
|
androidTestImplementation(libs.bundles.androidx.test)
|
||||||
|
|
||||||
|
androidTestImplementation(libs.zcashwalletplgn)
|
||||||
|
androidTestImplementation(libs.bip39)
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="cash.z.ecc.android.sdk.darkside">
|
||||||
|
|
||||||
|
<!-- For code coverage -->
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
<application android:name="androidx.multidex.MultiDexApplication" />
|
||||||
|
</manifest>
|
|
@ -1,8 +1,8 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside // package cash.z.ecc.android.sdk.integration
|
package cash.z.ecc.android.sdk.darkside // package cash.z.ecc.android.sdk.integration
|
||||||
//
|
//
|
||||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
// import cash.z.ecc.android.sdk.test.ScopedTest
|
||||||
// import cash.z.ecc.android.sdk.ext.twigTask
|
// import cash.z.ecc.android.sdk.ext.twigTask
|
||||||
// import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
// import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
|
||||||
// import kotlinx.coroutines.runBlocking
|
// import kotlinx.coroutines.runBlocking
|
||||||
// import org.junit.BeforeClass
|
// import org.junit.BeforeClass
|
||||||
// import org.junit.Test
|
// import org.junit.Test
|
||||||
|
@ -73,7 +73,7 @@ package cash.z.ecc.android.sdk.integration.darkside // package cash.z.ecc.androi
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// companion object {
|
// companion object {
|
||||||
// private val sithLord = DarksideTestCoordinator("192.168.1.134")
|
// private val sithLord = DarksideTestCoordinator()
|
||||||
// private val secondAddress = "zs15tzaulx5weua5c7l47l4pku2pw9fzwvvnsp4y80jdpul0y3nwn5zp7tmkcclqaca3mdjqjkl7hx"
|
// private val secondAddress = "zs15tzaulx5weua5c7l47l4pku2pw9fzwvvnsp4y80jdpul0y3nwn5zp7tmkcclqaca3mdjqjkl7hx"
|
||||||
// private val secondKey = "zxviews1q0w208wwqqqqpqyxp978kt2qgq5gcyx4er907zhczxpepnnhqn0a47ztefjnk65w2573v7g5fd3hhskrg7srpxazfvrj4n2gm4tphvr74a9xnenpaxy645dmuqkevkjtkf5jld2f7saqs3xyunwquhksjpqwl4zx8zj73m8gk2d5d30pck67v5hua8u3chwtxyetmzjya8jdjtyn2aum7au0agftfh5q9m4g596tev9k365s84jq8n3laa5f4palt330dq0yede053sdyfv6l"
|
// private val secondKey = "zxviews1q0w208wwqqqqpqyxp978kt2qgq5gcyx4er907zhczxpepnnhqn0a47ztefjnk65w2573v7g5fd3hhskrg7srpxazfvrj4n2gm4tphvr74a9xnenpaxy645dmuqkevkjtkf5jld2f7saqs3xyunwquhksjpqwl4zx8zj73m8gk2d5d30pck67v5hua8u3chwtxyetmzjya8jdjtyn2aum7au0agftfh5q9m4g596tev9k365s84jq8n3laa5f4palt330dq0yede053sdyfv6l"
|
||||||
//
|
//
|
|
@ -1,10 +1,10 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside
|
package cash.z.ecc.android.sdk.darkside
|
||||||
|
|
||||||
// import cash.z.ecc.android.sdk.SdkSynchronizer
|
// import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||||
// import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
// import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
||||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
// import cash.z.ecc.android.sdk.test.ScopedTest
|
||||||
// import cash.z.ecc.android.sdk.ext.twig
|
// import cash.z.ecc.android.sdk.ext.twig
|
||||||
// import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
// import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
|
||||||
// import kotlinx.coroutines.Job
|
// import kotlinx.coroutines.Job
|
||||||
// import kotlinx.coroutines.delay
|
// import kotlinx.coroutines.delay
|
||||||
// import kotlinx.coroutines.flow.launchIn
|
// import kotlinx.coroutines.flow.launchIn
|
||||||
|
@ -60,7 +60,7 @@ package cash.z.ecc.android.sdk.integration.darkside
|
||||||
//
|
//
|
||||||
// companion object {
|
// companion object {
|
||||||
// private const val blocksUrl = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/before-reorg.txt"
|
// private const val blocksUrl = "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/before-reorg.txt"
|
||||||
// private val sithLord = DarksideTestCoordinator("192.168.1.134")
|
// private val sithLord = DarksideTestCoordinator()
|
||||||
// private val secondAddress = "zs15tzaulx5weua5c7l47l4pku2pw9fzwvvnsp4y80jdpul0y3nwn5zp7tmkcclqaca3mdjqjkl7hx"
|
// private val secondAddress = "zs15tzaulx5weua5c7l47l4pku2pw9fzwvvnsp4y80jdpul0y3nwn5zp7tmkcclqaca3mdjqjkl7hx"
|
||||||
// private val secondKey = "zxviews1q0w208wwqqqqpqyxp978kt2qgq5gcyx4er907zhczxpepnnhqn0a47ztefjnk65w2573v7g5fd3hhskrg7srpxazfvrj4n2gm4tphvr74a9xnenpaxy645dmuqkevkjtkf5jld2f7saqs3xyunwquhksjpqwl4zx8zj73m8gk2d5d30pck67v5hua8u3chwtxyetmzjya8jdjtyn2aum7au0agftfh5q9m4g596tev9k365s84jq8n3laa5f4palt330dq0yede053sdyfv6l"
|
// private val secondKey = "zxviews1q0w208wwqqqqpqyxp978kt2qgq5gcyx4er907zhczxpepnnhqn0a47ztefjnk65w2573v7g5fd3hhskrg7srpxazfvrj4n2gm4tphvr74a9xnenpaxy645dmuqkevkjtkf5jld2f7saqs3xyunwquhksjpqwl4zx8zj73m8gk2d5d30pck67v5hua8u3chwtxyetmzjya8jdjtyn2aum7au0agftfh5q9m4g596tev9k365s84jq8n3laa5f4palt330dq0yede053sdyfv6l"
|
||||||
//
|
//
|
|
@ -1,10 +1,10 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside // package cash.z.ecc.android.sdk.integration
|
package cash.z.ecc.android.sdk.darkside // package cash.z.ecc.android.sdk.integration
|
||||||
//
|
//
|
||||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
// import cash.z.ecc.android.sdk.test.ScopedTest
|
||||||
// import cash.z.ecc.android.sdk.ext.twig
|
// import cash.z.ecc.android.sdk.ext.twig
|
||||||
// import cash.z.ecc.android.sdk.ext.twigTask
|
// import cash.z.ecc.android.sdk.ext.twigTask
|
||||||
// import cash.z.ecc.android.sdk.service.LightWalletGrpcService
|
// import cash.z.ecc.android.sdk.service.LightWalletGrpcService
|
||||||
// import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
// import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
|
||||||
// import cash.z.ecc.android.sdk.util.SimpleMnemonics
|
// import cash.z.ecc.android.sdk.util.SimpleMnemonics
|
||||||
// import cash.z.wallet.sdk.rpc.CompactFormats
|
// import cash.z.wallet.sdk.rpc.CompactFormats
|
||||||
// import cash.z.wallet.sdk.rpc.Service
|
// import cash.z.wallet.sdk.rpc.Service
|
||||||
|
@ -153,7 +153,7 @@ package cash.z.ecc.android.sdk.integration.darkside // package cash.z.ecc.androi
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// companion object {
|
// companion object {
|
||||||
// private val sithLord = DarksideTestCoordinator("192.168.1.134", "MultiRecipientInRust")
|
// private val sithLord = DarksideTestCoordinator(, "MultiRecipientInRust")
|
||||||
//
|
//
|
||||||
// private val randomPhrases = listOf(
|
// private val randomPhrases = listOf(
|
||||||
// "profit save black expose rude feature early rocket alter borrow finish october few duty flush kick spell bean burden enforce bitter theme silent uphold",
|
// "profit save black expose rude feature early rocket alter borrow finish october few duty flush kick spell bean burden enforce bitter theme silent uphold",
|
|
@ -1,7 +1,7 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside // package cash.z.ecc.android.sdk.integration
|
package cash.z.ecc.android.sdk.darkside // package cash.z.ecc.android.sdk.integration
|
||||||
//
|
//
|
||||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
// import cash.z.ecc.android.sdk.test.ScopedTest
|
||||||
// import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
// import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
|
||||||
// import org.junit.Before
|
// import org.junit.Before
|
||||||
// import org.junit.BeforeClass
|
// import org.junit.BeforeClass
|
||||||
// import org.junit.Test
|
// import org.junit.Test
|
|
@ -1,17 +1,16 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside
|
package cash.z.ecc.android.sdk.darkside
|
||||||
|
|
||||||
import cash.z.ecc.android.sdk.annotation.MaintainedTest
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import cash.z.ecc.android.sdk.annotation.TestPurpose.DARKSIDE
|
import cash.z.ecc.android.sdk.darkside.test.DarksideTest
|
||||||
import cash.z.ecc.android.sdk.annotation.TestPurpose.REGRESSION
|
|
||||||
import cash.z.ecc.android.sdk.ext.DarksideTest
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration test to run in order to catch any regressions in transparent behavior.
|
* Integration test to run in order to catch any regressions in transparent behavior.
|
||||||
*/
|
*/
|
||||||
@MaintainedTest(DARKSIDE, REGRESSION)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class TransparentIntegrationTest : DarksideTest() {
|
class TransparentIntegrationTest : DarksideTest() {
|
||||||
@Before
|
@Before
|
||||||
fun setup() = runOnce {
|
fun setup() = runOnce {
|
|
@ -1,11 +1,15 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs
|
package cash.z.ecc.android.sdk.darkside.reorgs
|
||||||
|
|
||||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import cash.z.ecc.android.sdk.darkside.test.ScopedTest
|
||||||
import cash.z.ecc.android.sdk.ext.twig
|
import cash.z.ecc.android.sdk.ext.twig
|
||||||
import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
|
||||||
import org.junit.BeforeClass
|
import org.junit.BeforeClass
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
class InboundTxTests : ScopedTest() {
|
class InboundTxTests : ScopedTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -78,7 +82,7 @@ class InboundTxTests : ScopedTest() {
|
||||||
private const val firstBlock = 663150
|
private const val firstBlock = 663150
|
||||||
private const val targetTxBlock = 663188
|
private const val targetTxBlock = 663188
|
||||||
private const val lastBlockHash = "2fc7b4682f5ba6ba6f86e170b40f0aa9302e1d3becb2a6ee0db611ff87835e4a"
|
private const val lastBlockHash = "2fc7b4682f5ba6ba6f86e170b40f0aa9302e1d3becb2a6ee0db611ff87835e4a"
|
||||||
private val sithLord = DarksideTestCoordinator("192.168.1.134")
|
private val sithLord = DarksideTestCoordinator()
|
||||||
private val validator = sithLord.validator
|
private val validator = sithLord.validator
|
||||||
private val chainMaker = sithLord.chainMaker
|
private val chainMaker = sithLord.chainMaker
|
||||||
|
|
||||||
|
@ -92,7 +96,8 @@ class InboundTxTests : ScopedTest() {
|
||||||
.stageEmptyBlocks(firstBlock + 1, 100)
|
.stageEmptyBlocks(firstBlock + 1, 100)
|
||||||
.applyTipHeight(targetTxBlock - 1)
|
.applyTipHeight(targetTxBlock - 1)
|
||||||
|
|
||||||
sithLord.startSync(classScope).await()
|
sithLord.synchronizer.start(classScope)
|
||||||
|
sithLord.await()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs // package cash.z.ecc.android.sdk.integration
|
package cash.z.ecc.android.sdk.darkside.reorgs // package cash.z.ecc.android.sdk.integration
|
||||||
//
|
//
|
||||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
// import cash.z.ecc.android.sdk.test.ScopedTest
|
||||||
// import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
// import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
|
||||||
// import org.junit.Assert.assertFalse
|
// import org.junit.Assert.assertFalse
|
||||||
// import org.junit.Assert.assertTrue
|
// import org.junit.Assert.assertTrue
|
||||||
// import org.junit.BeforeClass
|
// import org.junit.BeforeClass
|
|
@ -1,13 +1,13 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs // package cash.z.ecc.android.sdk.integration
|
package cash.z.ecc.android.sdk.darkside.reorgs // package cash.z.ecc.android.sdk.integration
|
||||||
//
|
//
|
||||||
// import androidx.test.platform.app.InstrumentationRegistry
|
// import androidx.test.platform.app.InstrumentationRegistry
|
||||||
// import cash.z.ecc.android.sdk.Initializer
|
// import cash.z.ecc.android.sdk.Initializer
|
||||||
// import cash.z.ecc.android.sdk.SdkSynchronizer
|
// import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||||
// import cash.z.ecc.android.sdk.Synchronizer
|
// import cash.z.ecc.android.sdk.Synchronizer
|
||||||
// import cash.z.ecc.android.sdk.ext.ScopedTest
|
// import cash.z.ecc.android.sdk.test.ScopedTest
|
||||||
// import cash.z.ecc.android.sdk.ext.import
|
// import cash.z.ecc.android.sdk.ext.import
|
||||||
// import cash.z.ecc.android.sdk.ext.twig
|
// import cash.z.ecc.android.sdk.ext.twig
|
||||||
// import cash.z.ecc.android.sdk.util.DarksideApi
|
// import cash.z.ecc.android.sdk.darkside.test.DarksideApi
|
||||||
// import io.grpc.StatusRuntimeException
|
// import io.grpc.StatusRuntimeException
|
||||||
// import kotlinx.coroutines.delay
|
// import kotlinx.coroutines.delay
|
||||||
// import kotlinx.coroutines.flow.filter
|
// import kotlinx.coroutines.flow.filter
|
||||||
|
@ -109,7 +109,6 @@ package cash.z.ecc.android.sdk.integration.darkside.reorgs // package cash.z.ecc
|
||||||
// get() = (synchronizer as SdkSynchronizer).processor.downloader.lightwalletService
|
// get() = (synchronizer as SdkSynchronizer).processor.downloader.lightwalletService
|
||||||
//
|
//
|
||||||
// companion object {
|
// companion object {
|
||||||
// private const val host = "192.168.1.134"
|
|
||||||
// private const val port = 9067
|
// private const val port = 9067
|
||||||
// private const val birthdayHeight = 663150
|
// private const val birthdayHeight = 663150
|
||||||
// private const val targetHeight = 663200
|
// private const val targetHeight = 663200
|
|
@ -1,11 +1,15 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs
|
package cash.z.ecc.android.sdk.darkside.reorgs
|
||||||
|
|
||||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
|
||||||
import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
|
||||||
|
import cash.z.ecc.android.sdk.darkside.test.ScopedTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.BeforeClass
|
import org.junit.BeforeClass
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
class ReorgSetupTest : ScopedTest() {
|
class ReorgSetupTest : ScopedTest() {
|
||||||
|
|
||||||
private val birthdayHeight = 663150
|
private val birthdayHeight = 663150
|
||||||
|
@ -30,7 +34,7 @@ class ReorgSetupTest : ScopedTest() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val sithLord = DarksideTestCoordinator("192.168.1.134")
|
private val sithLord = DarksideTestCoordinator()
|
||||||
private val validator = sithLord.validator
|
private val validator = sithLord.validator
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
|
@ -1,13 +1,16 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs
|
package cash.z.ecc.android.sdk.darkside.reorgs
|
||||||
|
|
||||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import cash.z.ecc.android.sdk.ext.twig
|
import cash.z.ecc.android.sdk.ext.twig
|
||||||
import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
|
||||||
|
import cash.z.ecc.android.sdk.darkside.test.ScopedTest
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.BeforeClass
|
import org.junit.BeforeClass
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
class ReorgSmallTest : ScopedTest() {
|
class ReorgSmallTest : ScopedTest() {
|
||||||
|
|
||||||
private val targetHeight = 663250
|
private val targetHeight = 663250
|
||||||
|
@ -41,7 +44,7 @@ class ReorgSmallTest : ScopedTest() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val sithLord = DarksideTestCoordinator("192.168.1.134")
|
private val sithLord = DarksideTestCoordinator()
|
||||||
private val validator = sithLord.validator
|
private val validator = sithLord.validator
|
||||||
private var hadReorg = false
|
private var hadReorg = false
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside.reorgs
|
package cash.z.ecc.android.sdk.darkside.reorgs
|
||||||
|
|
||||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import cash.z.ecc.android.sdk.ext.toHex
|
import cash.z.ecc.android.sdk.ext.toHex
|
||||||
import cash.z.ecc.android.sdk.util.DarksideTestCoordinator
|
import cash.z.ecc.android.sdk.darkside.test.DarksideTestCoordinator
|
||||||
import cash.z.ecc.android.sdk.util.SimpleMnemonics
|
import cash.z.ecc.android.sdk.darkside.test.ScopedTest
|
||||||
|
import cash.z.ecc.android.sdk.darkside.test.SimpleMnemonics
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
class SetupTest : ScopedTest() {
|
class SetupTest : ScopedTest() {
|
||||||
|
|
||||||
// @Test
|
// @Test
|
|
@ -1,11 +1,14 @@
|
||||||
package cash.z.ecc.android.sdk.integration.darkside.reproduce
|
package cash.z.ecc.android.sdk.darkside.reproduce
|
||||||
|
|
||||||
import cash.z.ecc.android.sdk.ext.DarksideTest
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import cash.z.ecc.android.sdk.darkside.test.DarksideTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.BeforeClass
|
import org.junit.BeforeClass
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
class ReproduceZ2TFailureTest : DarksideTest() {
|
class ReproduceZ2TFailureTest : DarksideTest() {
|
||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
|
@ -1,4 +1,4 @@
|
||||||
package cash.z.ecc.android.sdk.util
|
package cash.z.ecc.android.sdk.darkside.test
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import cash.z.ecc.android.sdk.R
|
import cash.z.ecc.android.sdk.R
|
|
@ -0,0 +1,18 @@
|
||||||
|
package cash.z.ecc.android.sdk.darkside.test
|
||||||
|
|
||||||
|
open class DarksideTest(name: String = javaClass.simpleName) : ScopedTest() {
|
||||||
|
val sithLord = DarksideTestCoordinator()
|
||||||
|
val validator = sithLord.validator
|
||||||
|
|
||||||
|
fun runOnce(block: () -> Unit) {
|
||||||
|
if (!ranOnce) {
|
||||||
|
sithLord.enterTheDarkside()
|
||||||
|
sithLord.synchronizer.start(classScope)
|
||||||
|
block()
|
||||||
|
ranOnce = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
companion object {
|
||||||
|
private var ranOnce = false
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,8 @@
|
||||||
package cash.z.ecc.android.sdk.util
|
package cash.z.ecc.android.sdk.darkside.test
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import cash.z.ecc.android.sdk.SdkSynchronizer
|
import cash.z.ecc.android.sdk.SdkSynchronizer
|
||||||
import cash.z.ecc.android.sdk.Synchronizer
|
import cash.z.ecc.android.sdk.Synchronizer
|
||||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
|
||||||
import cash.z.ecc.android.sdk.ext.seedPhrase
|
|
||||||
import cash.z.ecc.android.sdk.ext.twig
|
import cash.z.ecc.android.sdk.ext.twig
|
||||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||||
import io.grpc.StatusRuntimeException
|
import io.grpc.StatusRuntimeException
|
||||||
|
@ -24,10 +22,10 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
|
||||||
alias: String = "DarksideTestCoordinator",
|
alias: String = "DarksideTestCoordinator",
|
||||||
seedPhrase: String = DEFAULT_SEED_PHRASE,
|
seedPhrase: String = DEFAULT_SEED_PHRASE,
|
||||||
startHeight: Int = DEFAULT_START_HEIGHT,
|
startHeight: Int = DEFAULT_START_HEIGHT,
|
||||||
host: String = "127.0.0.1",
|
host: String = COMPUTER_LOCALHOST,
|
||||||
network: ZcashNetwork = ZcashNetwork.Mainnet,
|
network: ZcashNetwork = ZcashNetwork.Mainnet,
|
||||||
port: Int = network.defaultPort,
|
port: Int = network.defaultPort,
|
||||||
) : this(TestWallet(seedPhrase, alias, network, host, port, startHeight))
|
) : this(TestWallet(seedPhrase, alias, network, host, startHeight = startHeight, port = port))
|
||||||
|
|
||||||
private val targetHeight = 663250
|
private val targetHeight = 663250
|
||||||
private val context = InstrumentationRegistry.getInstrumentation().context
|
private val context = InstrumentationRegistry.getInstrumentation().context
|
||||||
|
@ -89,10 +87,6 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
|
||||||
// darkside.setBlocksUrl(largeReorg)
|
// darkside.setBlocksUrl(largeReorg)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
fun startSync(scope: CoroutineScope): DarksideTestCoordinator = apply {
|
|
||||||
synchronizer.start(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
// redo this as a call to wallet but add delay time to wallet join() function
|
// redo this as a call to wallet but add delay time to wallet join() function
|
||||||
/**
|
/**
|
||||||
* Waits for, at most, the given amount of time for the synchronizer to download and scan blocks
|
* Waits for, at most, the given amount of time for the synchronizer to download and scan blocks
|
||||||
|
@ -296,6 +290,12 @@ class DarksideTestCoordinator(val wallet: TestWallet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* This is a special localhost value on the Android emulator, which allows it to contact
|
||||||
|
* the localhost of the computer running the emulator.
|
||||||
|
*/
|
||||||
|
const val COMPUTER_LOCALHOST = "10.0.2.2"
|
||||||
|
|
||||||
// Block URLS
|
// Block URLS
|
||||||
private const val beforeReorg =
|
private const val beforeReorg =
|
||||||
"https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/before-reorg.txt"
|
"https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/before-reorg.txt"
|
|
@ -0,0 +1,48 @@
|
||||||
|
package cash.z.ecc.android.sdk.darkside.test
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.ApplicationInfo
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import org.junit.Before
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclass this to validate the environment for running Darkside tests.
|
||||||
|
*/
|
||||||
|
open class DarksideTestPrerequisites {
|
||||||
|
@Before
|
||||||
|
fun verifyEmulator() {
|
||||||
|
require(isProbablyEmulator(ApplicationProvider.getApplicationContext())) {
|
||||||
|
"Darkside tests are configured to only run on the Android Emulator. Please see https://github.com/zcash/zcash-android-wallet-sdk/blob/master/docs/tests/Darkside.md"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun isProbablyEmulator(context: Context): Boolean {
|
||||||
|
if (isDebuggable(context)) {
|
||||||
|
// This is imperfect and could break in the future
|
||||||
|
if (null == Build.DEVICE
|
||||||
|
|| "generic" == Build.DEVICE //$NON-NLS
|
||||||
|
|| ("generic_x86" == Build.DEVICE) //$NON-NLS
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether the application running is debuggable. This is determined from the
|
||||||
|
* ApplicationInfo object (`BuildInfo` is useless for libraries.)
|
||||||
|
*/
|
||||||
|
private fun isDebuggable(context: Context): Boolean {
|
||||||
|
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||||
|
|
||||||
|
// Normally shouldn't be null, but could be with a MockContext
|
||||||
|
return packageInfo.applicationInfo?.let {
|
||||||
|
0 != (it.flags and ApplicationInfo.FLAG_DEBUGGABLE)
|
||||||
|
} ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package cash.z.ecc.android.sdk.darkside.test
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
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 kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.newFixedThreadPoolContext
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.AfterClass
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
|
open class ScopedTest(val defaultTimeout: Long = 2000L) : DarksideTestPrerequisites() {
|
||||||
|
protected lateinit var testScope: CoroutineScope
|
||||||
|
|
||||||
|
// if an androidTest doesn't need a context, then maybe it should be a unit test instead?!
|
||||||
|
val context: Context = InstrumentationRegistry.getInstrumentation().context
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun start() {
|
||||||
|
twig("===================== TEST STARTED ==================================")
|
||||||
|
testScope = CoroutineScope(
|
||||||
|
Job(classScope.coroutineContext[Job]!!) + newFixedThreadPoolContext(
|
||||||
|
5,
|
||||||
|
this.javaClass.simpleName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun end() = runBlocking<Unit> {
|
||||||
|
twig("======================= TEST CANCELLING =============================")
|
||||||
|
testScope.cancel()
|
||||||
|
testScope.coroutineContext[Job]?.join()
|
||||||
|
twig("======================= TEST ENDED ==================================")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun timeout(duration: Long, block: suspend () -> Unit) = timeoutWith(testScope, duration, block)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
lateinit var classScope: CoroutineScope
|
||||||
|
|
||||||
|
init {
|
||||||
|
Twig.plant(TroubleshootingTwig())
|
||||||
|
twig("================================================================ INIT")
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun createScope() {
|
||||||
|
twig("======================= CLASS STARTED ===============================")
|
||||||
|
classScope = CoroutineScope(
|
||||||
|
SupervisorJob() + newFixedThreadPoolContext(2, this.javaClass.simpleName)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
@JvmStatic
|
||||||
|
fun destroyScope() = runBlocking<Unit> {
|
||||||
|
twig("======================= CLASS CANCELLING ============================")
|
||||||
|
classScope.cancel()
|
||||||
|
classScope.coroutineContext[Job]?.join()
|
||||||
|
twig("======================= CLASS ENDED =================================")
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun timeoutWith(scope: CoroutineScope, duration: Long, block: suspend () -> Unit) {
|
||||||
|
scope.launch {
|
||||||
|
delay(duration)
|
||||||
|
val message = "ERROR: Test timed out after ${duration}ms"
|
||||||
|
twig(message)
|
||||||
|
throw TimeoutException(message)
|
||||||
|
}.let { selfDestruction ->
|
||||||
|
scope.launch {
|
||||||
|
block()
|
||||||
|
selfDestruction.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package cash.z.ecc.android.sdk.darkside.test
|
||||||
|
|
||||||
|
import cash.z.android.plugin.MnemonicPlugin
|
||||||
|
import cash.z.ecc.android.bip39.Mnemonics
|
||||||
|
import cash.z.ecc.android.bip39.Mnemonics.MnemonicCode
|
||||||
|
import cash.z.ecc.android.bip39.Mnemonics.WordCount
|
||||||
|
import cash.z.ecc.android.bip39.toEntropy
|
||||||
|
import cash.z.ecc.android.bip39.toSeed
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class SimpleMnemonics : MnemonicPlugin {
|
||||||
|
override fun fullWordList(languageCode: String) = Mnemonics.getCachedWords(Locale.ENGLISH.language)
|
||||||
|
override fun nextEntropy(): ByteArray = WordCount.COUNT_24.toEntropy()
|
||||||
|
override fun nextMnemonic(): CharArray = MnemonicCode(WordCount.COUNT_24).chars
|
||||||
|
override fun nextMnemonic(entropy: ByteArray): CharArray = MnemonicCode(entropy).chars
|
||||||
|
override fun nextMnemonicList(): List<CharArray> = MnemonicCode(WordCount.COUNT_24).words
|
||||||
|
override fun nextMnemonicList(entropy: ByteArray): List<CharArray> = MnemonicCode(entropy).words
|
||||||
|
override fun toSeed(mnemonic: CharArray): ByteArray = MnemonicCode(mnemonic).toSeed()
|
||||||
|
override fun toWordList(mnemonic: CharArray): List<CharArray> = MnemonicCode(mnemonic).words
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
package cash.z.ecc.android.sdk.darkside.test
|
||||||
|
|
||||||
|
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(
|
||||||
|
val seedPhrase: String,
|
||||||
|
val alias: String = "TestWallet",
|
||||||
|
val network: ZcashNetwork = ZcashNetwork.Testnet,
|
||||||
|
val host: String = network.defaultHost,
|
||||||
|
startHeight: Int? = null,
|
||||||
|
val port: Int = network.defaultPort,
|
||||||
|
) {
|
||||||
|
constructor(
|
||||||
|
backup: Backups,
|
||||||
|
network: ZcashNetwork = ZcashNetwork.Testnet,
|
||||||
|
alias: String = "TestWallet"
|
||||||
|
) : this(
|
||||||
|
backup.seedPhrase,
|
||||||
|
network = network,
|
||||||
|
startHeight = if (network == ZcashNetwork.Mainnet) backup.mainnetBirthday else backup.testnetBirthday,
|
||||||
|
alias = alias
|
||||||
|
)
|
||||||
|
|
||||||
|
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, host, alias = alias)
|
||||||
|
}
|
||||||
|
val synchronizer: SdkSynchronizer = Synchronizer(initializer) as SdkSynchronizer
|
||||||
|
val service = (synchronizer.processor.downloader.lightWalletService as LightWalletGrpcService)
|
||||||
|
|
||||||
|
val available get() = synchronizer.saplingBalances.value.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, fromAccountIndex: Int = 0): TestWallet {
|
||||||
|
Twig.sprout("$alias sending")
|
||||||
|
synchronizer.sendToAddress(shieldedSpendingKey, amount, address, memo, fromAccountIndex)
|
||||||
|
.takeWhile { it.isPending() }
|
||||||
|
.collect {
|
||||||
|
twig("Updated transaction: $it")
|
||||||
|
}
|
||||||
|
Twig.clip("$alias sending")
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun rewindToHeight(height: Int): TestWallet {
|
||||||
|
synchronizer.rewindToNearestHeight(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, val mainnetBirthday: Int) {
|
||||||
|
// TODO: get the proper birthday values for these wallets
|
||||||
|
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, 1_000_000),
|
||||||
|
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, 1_000_000),
|
||||||
|
DEV_WALLET("still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread", 1_000_000, 991645),
|
||||||
|
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, 1_000_000),
|
||||||
|
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_000_000),
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="cash.z.ecc.android.sdk.darkside">
|
||||||
|
|
||||||
|
<application android:name="androidx.multidex.MultiDexApplication" />
|
||||||
|
</manifest>
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<bool name="lightwalletd_allow_very_insecure_connections">true</bool>
|
||||||
|
</resources>
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Running Darksidewalletd tests
|
||||||
|
Some tests are executed against a fake version of the Zcash network, by running a localhost lightwalletd server in a special mode called "darkside". This is different from the Zcash test network, which is a publicly accessible and deployed network that acts more like a staging network before changes are pushed to the production network.
|
||||||
|
|
||||||
|
The module [darkside-test-lib](../../darkside-test-lib) contains a test suite that requires manually launching a localhost lightwalletd instance in darkside mode.
|
||||||
|
|
||||||
|
To run these tests
|
||||||
|
|
||||||
|
1. clone [lightwalletd](https://github.com/zcash/lightwalletd.git)
|
||||||
|
`git clone https://github.com/zcash/lightwalletd.git`
|
||||||
|
1. Install Go.
|
||||||
|
1. If you're using homebrew
|
||||||
|
```` zsh
|
||||||
|
brew install go
|
||||||
|
````
|
||||||
|
1. Inside the `lightwalletd` checkout, compile lightwalletd
|
||||||
|
```` zsh
|
||||||
|
make
|
||||||
|
````
|
||||||
|
|
||||||
|
1. Inside the `lightwalletd` checkout, run the program in _darkside_ mode
|
||||||
|
```` zsh
|
||||||
|
./lightwalletd --log-file /dev/stdout --darkside-very-insecure --darkside-timeout 1000 --gen-cert-very-insecure --data-dir . --no-tls-very-insecure
|
||||||
|
````
|
||||||
|
1. Launch an Android emulator. Darkside tests are configured to only run on an Android emulator, as this makes it easy to automate finding the localhost server running on the same computer that's also running the emulator.
|
||||||
|
1. Run the Android test suite
|
||||||
|
1. From the command line
|
||||||
|
```` zsh
|
||||||
|
./gradlew :darkside-test-lib:connectedAndroidTest
|
||||||
|
````
|
||||||
|
1. From Android Studio
|
||||||
|
1. Choose the run configuration `:darkside-test-lib:connectedAndroidTest`
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="cash.z.ecc.android.sdk">
|
||||||
|
|
||||||
|
<!-- For code coverage -->
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
<application android:name="androidx.multidex.MultiDexApplication" />
|
||||||
|
</manifest>
|
|
@ -1,125 +1,17 @@
|
||||||
package cash.z.ecc.android.sdk.ext
|
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.Initializer
|
||||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
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 cash.z.ecc.android.sdk.util.SimpleMnemonics
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.newFixedThreadPoolContext
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import org.json.JSONObject
|
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 ru.gildor.coroutines.okhttp.await
|
||||||
import java.util.concurrent.TimeoutException
|
|
||||||
|
|
||||||
fun Initializer.Config.seedPhrase(seedPhrase: String, network: ZcashNetwork) {
|
fun Initializer.Config.seedPhrase(seedPhrase: String, network: ZcashNetwork) {
|
||||||
setSeed(SimpleMnemonics().toSeed(seedPhrase.toCharArray()), network)
|
setSeed(SimpleMnemonics().toSeed(seedPhrase.toCharArray()), network)
|
||||||
}
|
}
|
||||||
|
|
||||||
open class ScopedTest(val defaultTimeout: Long = 2000L) {
|
|
||||||
protected lateinit var testScope: CoroutineScope
|
|
||||||
|
|
||||||
// if an androidTest doesn't need a context, then maybe it should be a unit test instead?!
|
|
||||||
val context: Context = InstrumentationRegistry.getInstrumentation().context
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun start() {
|
|
||||||
twig("===================== TEST STARTED ==================================")
|
|
||||||
testScope = CoroutineScope(
|
|
||||||
Job(classScope.coroutineContext[Job]!!) + newFixedThreadPoolContext(
|
|
||||||
5,
|
|
||||||
this.javaClass.simpleName
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun end() = runBlocking<Unit> {
|
|
||||||
twig("======================= TEST CANCELLING =============================")
|
|
||||||
testScope.cancel()
|
|
||||||
testScope.coroutineContext[Job]?.join()
|
|
||||||
twig("======================= TEST ENDED ==================================")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun timeout(duration: Long, block: suspend () -> Unit) = timeoutWith(testScope, duration, block)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
lateinit var classScope: CoroutineScope
|
|
||||||
|
|
||||||
init {
|
|
||||||
Twig.plant(TroubleshootingTwig())
|
|
||||||
twig("================================================================ INIT")
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
@JvmStatic
|
|
||||||
fun createScope() {
|
|
||||||
twig("======================= CLASS STARTED ===============================")
|
|
||||||
classScope = CoroutineScope(
|
|
||||||
SupervisorJob() + newFixedThreadPoolContext(2, this.javaClass.simpleName)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterClass
|
|
||||||
@JvmStatic
|
|
||||||
fun destroyScope() = runBlocking<Unit> {
|
|
||||||
twig("======================= CLASS CANCELLING ============================")
|
|
||||||
classScope.cancel()
|
|
||||||
classScope.coroutineContext[Job]?.join()
|
|
||||||
twig("======================= CLASS ENDED =================================")
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun timeoutWith(scope: CoroutineScope, duration: Long, block: suspend () -> Unit) {
|
|
||||||
scope.launch {
|
|
||||||
delay(duration)
|
|
||||||
val message = "ERROR: Test timed out after ${duration}ms"
|
|
||||||
twig(message)
|
|
||||||
throw TimeoutException(message)
|
|
||||||
}.let { selfDestruction ->
|
|
||||||
scope.launch {
|
|
||||||
block()
|
|
||||||
selfDestruction.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
object BlockExplorer {
|
||||||
suspend fun fetchLatestHeight(): Int {
|
suspend fun fetchLatestHeight(): Int {
|
||||||
val client = OkHttpClient()
|
val client = OkHttpClient()
|
||||||
|
|
|
@ -6,13 +6,13 @@ import cash.z.ecc.android.sdk.Initializer
|
||||||
import cash.z.ecc.android.sdk.Synchronizer
|
import cash.z.ecc.android.sdk.Synchronizer
|
||||||
import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED
|
import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED
|
||||||
import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
import cash.z.ecc.android.sdk.db.entity.isSubmitSuccess
|
||||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
|
||||||
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
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.ext.ZcashSdk
|
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||||
import cash.z.ecc.android.sdk.ext.onFirst
|
import cash.z.ecc.android.sdk.ext.onFirst
|
||||||
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.service.LightWalletGrpcService
|
||||||
|
import cash.z.ecc.android.sdk.test.ScopedTest
|
||||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||||
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
|
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
|
||||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||||
|
|
|
@ -8,10 +8,10 @@ import cash.z.ecc.android.sdk.block.CompactBlockDownloader
|
||||||
import cash.z.ecc.android.sdk.block.CompactBlockStore
|
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.ChainInfoNotMatching
|
||||||
import cash.z.ecc.android.sdk.exception.LightWalletException.ChangeServerException.StatusException
|
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.twig
|
import cash.z.ecc.android.sdk.ext.twig
|
||||||
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
|
import cash.z.ecc.android.sdk.service.LightWalletGrpcService
|
||||||
import cash.z.ecc.android.sdk.service.LightWalletService
|
import cash.z.ecc.android.sdk.service.LightWalletService
|
||||||
|
import cash.z.ecc.android.sdk.test.ScopedTest
|
||||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
package cash.z.ecc.android.sdk.test
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
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 kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.newFixedThreadPoolContext
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.AfterClass
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import java.util.concurrent.TimeoutException
|
||||||
|
|
||||||
|
open class ScopedTest(val defaultTimeout: Long = 2000L) {
|
||||||
|
protected lateinit var testScope: CoroutineScope
|
||||||
|
|
||||||
|
// if an androidTest doesn't need a context, then maybe it should be a unit test instead?!
|
||||||
|
val context: Context = InstrumentationRegistry.getInstrumentation().context
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun start() {
|
||||||
|
twig("===================== TEST STARTED ==================================")
|
||||||
|
testScope = CoroutineScope(
|
||||||
|
Job(classScope.coroutineContext[Job]!!) + newFixedThreadPoolContext(
|
||||||
|
5,
|
||||||
|
this.javaClass.simpleName
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun end() = runBlocking<Unit> {
|
||||||
|
twig("======================= TEST CANCELLING =============================")
|
||||||
|
testScope.cancel()
|
||||||
|
testScope.coroutineContext[Job]?.join()
|
||||||
|
twig("======================= TEST ENDED ==================================")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun timeout(duration: Long, block: suspend () -> Unit) = timeoutWith(testScope, duration, block)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
lateinit var classScope: CoroutineScope
|
||||||
|
|
||||||
|
init {
|
||||||
|
Twig.plant(TroubleshootingTwig())
|
||||||
|
twig("================================================================ INIT")
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun createScope() {
|
||||||
|
twig("======================= CLASS STARTED ===============================")
|
||||||
|
classScope = CoroutineScope(
|
||||||
|
SupervisorJob() + newFixedThreadPoolContext(2, this.javaClass.simpleName)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
@JvmStatic
|
||||||
|
fun destroyScope() = runBlocking<Unit> {
|
||||||
|
twig("======================= CLASS CANCELLING ============================")
|
||||||
|
classScope.cancel()
|
||||||
|
classScope.coroutineContext[Job]?.join()
|
||||||
|
twig("======================= CLASS ENDED =================================")
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun timeoutWith(scope: CoroutineScope, duration: Long, block: suspend () -> Unit) {
|
||||||
|
scope.launch {
|
||||||
|
delay(duration)
|
||||||
|
val message = "ERROR: Test timed out after ${duration}ms"
|
||||||
|
twig(message)
|
||||||
|
throw TimeoutException(message)
|
||||||
|
}.let { selfDestruction ->
|
||||||
|
scope.launch {
|
||||||
|
block()
|
||||||
|
selfDestruction.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,11 +7,11 @@ 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.EncodedTransaction
|
||||||
import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
import cash.z.ecc.android.sdk.db.entity.PendingTransaction
|
||||||
import cash.z.ecc.android.sdk.db.entity.isCancelled
|
import cash.z.ecc.android.sdk.db.entity.isCancelled
|
||||||
import cash.z.ecc.android.sdk.ext.ScopedTest
|
|
||||||
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
|
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.ext.twig
|
import cash.z.ecc.android.sdk.ext.twig
|
||||||
import cash.z.ecc.android.sdk.service.LightWalletService
|
import cash.z.ecc.android.sdk.service.LightWalletService
|
||||||
|
import cash.z.ecc.android.sdk.test.ScopedTest
|
||||||
import com.nhaarman.mockitokotlin2.any
|
import com.nhaarman.mockitokotlin2.any
|
||||||
import com.nhaarman.mockitokotlin2.stub
|
import com.nhaarman.mockitokotlin2.stub
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
|
|
|
@ -141,7 +141,6 @@ dependencyResolutionManagement {
|
||||||
listOf(
|
listOf(
|
||||||
"androidx-espresso-core",
|
"androidx-espresso-core",
|
||||||
"androidx-espresso-intents",
|
"androidx-espresso-intents",
|
||||||
"androidx-espresso-contrib",
|
|
||||||
"androidx-test-junit",
|
"androidx-test-junit",
|
||||||
"androidx-test-core"
|
"androidx-test-core"
|
||||||
)
|
)
|
||||||
|
@ -173,5 +172,6 @@ rootProject.name = "zcash-android-sdk"
|
||||||
|
|
||||||
includeBuild("build-conventions")
|
includeBuild("build-conventions")
|
||||||
|
|
||||||
|
include("darkside-test-lib")
|
||||||
include("sdk-lib")
|
include("sdk-lib")
|
||||||
include("demo-app")
|
include("demo-app")
|
Loading…
Reference in New Issue