[#1136] Tests for the new helper extensions

* Jni objects attribute constraint tests


* Extend continuity error test

* ScanRange model tests

* [#1174] Move model classes out of the CompactBlockProcessor

* [#1174] SbS: Move model classes out of the CompactBlockProcessor

* Move SyncingResult out of the processor

* Add issue link
This commit is contained in:
Honza Rychnovský 2023-08-18 13:25:54 +02:00 committed by Honza
parent 7277a7ecca
commit 9795610bb9
24 changed files with 304 additions and 121 deletions

View File

@ -0,0 +1,32 @@
package cash.z.ecc.android.sdk.internal.model
import kotlin.test.Test
import kotlin.test.assertFailsWith
import kotlin.test.assertIs
class JniBlockMetaTest {
@Test
fun attributes_within_constraints() {
val instance = JniBlockMeta(
height = UInt.MAX_VALUE.toLong(),
hash = byteArrayOf(),
time = 0L,
saplingOutputsCount = UInt.MIN_VALUE.toLong(),
orchardOutputsCount = UInt.MIN_VALUE.toLong()
)
assertIs<JniBlockMeta>(instance)
}
@Test
fun attributes_not_in_constraints() {
assertFailsWith(IllegalArgumentException::class) {
JniBlockMeta(
height = Long.MAX_VALUE,
hash = byteArrayOf(),
time = 0L,
saplingOutputsCount = Long.MIN_VALUE,
orchardOutputsCount = Long.MIN_VALUE
)
}
}
}

View File

@ -0,0 +1,28 @@
package cash.z.ecc.android.sdk.internal.model
import kotlin.test.Test
import kotlin.test.assertFailsWith
import kotlin.test.assertIs
class JniScanRangeTest {
@Test
fun attributes_within_constraints() {
val instance = JniScanRange(
startHeight = UInt.MIN_VALUE.toLong(),
endHeight = UInt.MAX_VALUE.toLong(),
priority = 10
)
assertIs<JniScanRange>(instance)
}
@Test
fun attributes_not_in_constraints() {
assertFailsWith(IllegalArgumentException::class) {
JniScanRange(
startHeight = Long.MIN_VALUE,
endHeight = Long.MAX_VALUE,
priority = 10
)
}
}
}

View File

@ -0,0 +1,26 @@
package cash.z.ecc.android.sdk.internal.model
import kotlin.test.Test
import kotlin.test.assertFailsWith
import kotlin.test.assertIs
class JniSubtreeRootTest {
@Test
fun attributes_within_constraints() {
val instance = JniSubtreeRoot(
rootHash = byteArrayOf(),
completingBlockHeight = UInt.MAX_VALUE.toLong()
)
assertIs<JniSubtreeRoot>(instance)
}
@Test
fun attributes_not_in_constraints() {
assertFailsWith(IllegalArgumentException::class) {
JniSubtreeRoot(
rootHash = byteArrayOf(),
completingBlockHeight = Long.MAX_VALUE
)
}
}
}

View File

@ -1,7 +1,7 @@
package cash.z.ecc.android.sdk.demoapp.fixture package cash.z.ecc.android.sdk.demoapp.fixture
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.block.CompactBlockProcessor import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.SynchronizerError import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.SynchronizerError
import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.WalletSnapshot import cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel.WalletSnapshot
import cash.z.ecc.android.sdk.model.PercentDecimal import cash.z.ecc.android.sdk.model.PercentDecimal

View File

@ -1,7 +1,7 @@
package cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel package cash.z.ecc.android.sdk.demoapp.ui.screen.home.viewmodel
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.block.CompactBlockProcessor import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
import cash.z.ecc.android.sdk.ext.ZcashSdk import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.model.PercentDecimal import cash.z.ecc.android.sdk.model.PercentDecimal
import cash.z.ecc.android.sdk.model.WalletBalance import cash.z.ecc.android.sdk.model.WalletBalance

View File

@ -7,7 +7,7 @@ import cash.z.ecc.android.bip39.Mnemonics
import cash.z.ecc.android.bip39.toSeed import cash.z.ecc.android.bip39.toSeed
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.WalletCoordinator import cash.z.ecc.android.sdk.WalletCoordinator
import cash.z.ecc.android.sdk.block.CompactBlockProcessor import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
import cash.z.ecc.android.sdk.demoapp.getInstance import cash.z.ecc.android.sdk.demoapp.getInstance
import cash.z.ecc.android.sdk.demoapp.preference.EncryptedPreferenceKeys import cash.z.ecc.android.sdk.demoapp.preference.EncryptedPreferenceKeys
import cash.z.ecc.android.sdk.demoapp.preference.EncryptedPreferenceSingleton import cash.z.ecc.android.sdk.demoapp.preference.EncryptedPreferenceSingleton

View File

@ -1,7 +1,7 @@
package cash.z.ecc.android.sdk package cash.z.ecc.android.sdk
import android.content.Context import android.content.Context
import cash.z.ecc.android.sdk.block.CompactBlockProcessor import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
import cash.z.ecc.android.sdk.ext.onFirst import cash.z.ecc.android.sdk.ext.onFirst
import cash.z.ecc.android.sdk.internal.Twig import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.model.PersistableWallet import cash.z.ecc.android.sdk.model.PersistableWallet

View File

@ -0,0 +1,8 @@
package cash.z.ecc.android.sdk.block.processor
// TODO [#1094]: Consider fake SDK sync related components
// TODO [#1094]: Testing the CompactBlockProcessor is only available once we can mock the necessary core components like
// [CompactBlockDownloader], [DerivedDataRepository], or [TypesafeBackend]
// TODO [#1094]: https://github.com/zcash/zcash-android-wallet-sdk/issues/1094
@Suppress("EmptyClassBlock")
class CompactBlockProcessorTest

View File

@ -5,12 +5,12 @@ import cash.z.ecc.android.sdk.Synchronizer.Status.DISCONNECTED
import cash.z.ecc.android.sdk.Synchronizer.Status.STOPPED import cash.z.ecc.android.sdk.Synchronizer.Status.STOPPED
import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCED
import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCING import cash.z.ecc.android.sdk.Synchronizer.Status.SYNCING
import cash.z.ecc.android.sdk.block.CompactBlockProcessor import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
import cash.z.ecc.android.sdk.block.CompactBlockProcessor.State.Disconnected import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor.State.Disconnected
import cash.z.ecc.android.sdk.block.CompactBlockProcessor.State.Initialized import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor.State.Initialized
import cash.z.ecc.android.sdk.block.CompactBlockProcessor.State.Stopped import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor.State.Stopped
import cash.z.ecc.android.sdk.block.CompactBlockProcessor.State.Synced import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor.State.Synced
import cash.z.ecc.android.sdk.block.CompactBlockProcessor.State.Syncing import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor.State.Syncing
import cash.z.ecc.android.sdk.exception.TransactionEncoderException import cash.z.ecc.android.sdk.exception.TransactionEncoderException
import cash.z.ecc.android.sdk.exception.TransactionSubmitException import cash.z.ecc.android.sdk.exception.TransactionSubmitException
import cash.z.ecc.android.sdk.ext.ConsensusBranchId import cash.z.ecc.android.sdk.ext.ConsensusBranchId

View File

@ -1,7 +1,7 @@
package cash.z.ecc.android.sdk package cash.z.ecc.android.sdk
import android.content.Context import android.content.Context
import cash.z.ecc.android.sdk.block.CompactBlockProcessor import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
import cash.z.ecc.android.sdk.ext.ZcashSdk import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.internal.Derivation import cash.z.ecc.android.sdk.internal.Derivation
import cash.z.ecc.android.sdk.internal.SaplingParamTool import cash.z.ecc.android.sdk.internal.SaplingParamTool

View File

@ -1,8 +1,16 @@
package cash.z.ecc.android.sdk.block package cash.z.ecc.android.sdk.block.processor
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import cash.z.ecc.android.sdk.BuildConfig import cash.z.ecc.android.sdk.BuildConfig
import cash.z.ecc.android.sdk.annotation.OpenForTesting import cash.z.ecc.android.sdk.annotation.OpenForTesting
import cash.z.ecc.android.sdk.block.processor.model.BatchSyncProgress
import cash.z.ecc.android.sdk.block.processor.model.GetSubtreeRootsResult
import cash.z.ecc.android.sdk.block.processor.model.PutSaplingSubtreeRootsResult
import cash.z.ecc.android.sdk.block.processor.model.SuggestScanRangesResult
import cash.z.ecc.android.sdk.block.processor.model.SyncStageResult
import cash.z.ecc.android.sdk.block.processor.model.SyncingResult
import cash.z.ecc.android.sdk.block.processor.model.UpdateChainTipResult
import cash.z.ecc.android.sdk.block.processor.model.VerifySuggestedScanRange
import cash.z.ecc.android.sdk.exception.CompactBlockProcessorException import cash.z.ecc.android.sdk.exception.CompactBlockProcessorException
import cash.z.ecc.android.sdk.exception.CompactBlockProcessorException.EnhanceTransactionError.EnhanceTxDecryptError import cash.z.ecc.android.sdk.exception.CompactBlockProcessorException.EnhanceTransactionError.EnhanceTxDecryptError
import cash.z.ecc.android.sdk.exception.CompactBlockProcessorException.EnhanceTransactionError.EnhanceTxDownloadError import cash.z.ecc.android.sdk.exception.CompactBlockProcessorException.EnhanceTransactionError.EnhanceTxDownloadError
@ -258,8 +266,6 @@ class CompactBlockProcessor internal constructor(
} }
BlockProcessingResult.NoBlocksToProcess -> { BlockProcessingResult.NoBlocksToProcess -> {
setState(State.Synced(_processorInfo.value.overallSyncRange)) setState(State.Synced(_processorInfo.value.overallSyncRange))
// TODO [#1129]: Refactor work with lastSyncRange and lastSyncedHeight
// TODO [#1129]: https://github.com/zcash/zcash-android-wallet-sdk/issues/1129
val noWorkDone = _processorInfo.value.overallSyncRange?.isEmpty() ?: true val noWorkDone = _processorInfo.value.overallSyncRange?.isEmpty() ?: true
val summary = if (noWorkDone) { val summary = if (noWorkDone) {
"Nothing to process: no new blocks to sync" "Nothing to process: no new blocks to sync"
@ -354,34 +360,6 @@ class CompactBlockProcessor internal constructor(
} }
} }
internal sealed class SuggestScanRangesResult {
data class Success(val ranges: List<ScanRange>) : SuggestScanRangesResult()
data class Failure(val failedAtHeight: BlockHeight, val exception: Throwable) : SuggestScanRangesResult()
}
internal sealed class GetSubtreeRootsResult {
// SbS: Spend-before-Sync
data class UseSbS(val subTreeRootList: List<SubtreeRoot>) : GetSubtreeRootsResult()
object UseLinear : GetSubtreeRootsResult()
object FailureConnection : GetSubtreeRootsResult()
data class OtherFailure(val exception: Throwable) : GetSubtreeRootsResult()
}
internal sealed class PutSaplingSubtreeRootsResult {
object Success : PutSaplingSubtreeRootsResult()
data class Failure(val failedAtHeight: BlockHeight, val exception: Throwable) : PutSaplingSubtreeRootsResult()
}
internal sealed class UpdateChainTipResult {
data class Success(val height: BlockHeight) : UpdateChainTipResult()
data class Failure(val failedAtHeight: BlockHeight, val exception: Throwable) : UpdateChainTipResult()
}
internal sealed class VerifySuggestedScanRange {
data class ShouldVerify(val scanRange: ScanRange) : VerifySuggestedScanRange()
object NoRangeToVerify : VerifySuggestedScanRange()
}
// TODO [#1137]: Refactor processNewBlocksInSbSOrder // TODO [#1137]: Refactor processNewBlocksInSbSOrder
// TODO [#1137]: https://github.com/zcash/zcash-android-wallet-sdk/issues/1137 // TODO [#1137]: https://github.com/zcash/zcash-android-wallet-sdk/issues/1137
/** /**
@ -1192,49 +1170,6 @@ class CompactBlockProcessor internal constructor(
} }
} }
@VisibleForTesting
internal sealed class SyncingResult {
object AllSuccess : SyncingResult()
data class DownloadSuccess(val downloadedBlocks: List<JniBlockMeta>?) : SyncingResult() {
override fun toString(): String {
return "DownloadSuccess with ${downloadedBlocks?.size ?: "none"} blocks"
}
}
interface Failure {
val failedAtHeight: BlockHeight
val exception: CompactBlockProcessorException
fun toBlockProcessingResult(): BlockProcessingResult =
BlockProcessingResult.SyncFailure(
this.failedAtHeight,
this.exception
)
}
data class DownloadFailed(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
object ScanSuccess : SyncingResult()
data class ScanFailed(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
object DeleteSuccess : SyncingResult()
data class DeleteFailed(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
object EnhanceSuccess : SyncingResult()
data class EnhanceFailed(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
object UpdateBirthday : SyncingResult()
data class ContinuityError(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
}
/** /**
* Requests, processes and persists all blocks from the given range. * Requests, processes and persists all blocks from the given range.
* *
@ -2034,23 +1969,6 @@ class CompactBlockProcessor internal constructor(
object Initialized : State() object Initialized : State()
} }
/**
* Progress model class for sharing the whole batch sync progress out of the sync process.
*/
internal data class BatchSyncProgress(
val inRangeOrder: Long = 0,
val overallOrder: Long = 0,
val resultState: SyncingResult = SyncingResult.AllSuccess
)
/**
* Progress model class for sharing particular sync stage result internally in the sync process.
*/
private data class SyncStageResult(
val batch: BlockBatch,
val stageResult: SyncingResult
)
/** /**
* Data class for holding detailed information about the processor. * Data class for holding detailed information about the processor.
* *

View File

@ -0,0 +1,11 @@
package cash.z.ecc.android.sdk.block.processor.model
/**
* Progress model class for sharing the whole batch synchronization progress out of the synchronization process.
*/
internal data class BatchSyncProgress(
val inRangeOrder: Long = 0,
val overallOrder: Long = 0,
val resultState: SyncingResult =
SyncingResult.AllSuccess
)

View File

@ -0,0 +1,14 @@
package cash.z.ecc.android.sdk.block.processor.model
import cash.z.ecc.android.sdk.internal.model.SubtreeRoot
/**
* Internal class for get subtree roots action result.
*/
internal sealed class GetSubtreeRootsResult {
// SbS: Spend-before-Sync
data class UseSbS(val subTreeRootList: List<SubtreeRoot>) : GetSubtreeRootsResult()
object UseLinear : GetSubtreeRootsResult()
object FailureConnection : GetSubtreeRootsResult()
data class OtherFailure(val exception: Throwable) : GetSubtreeRootsResult()
}

View File

@ -0,0 +1,11 @@
package cash.z.ecc.android.sdk.block.processor.model
import cash.z.ecc.android.sdk.model.BlockHeight
/**
* Internal class for sharing put sapling subtree roots action result.
*/
internal sealed class PutSaplingSubtreeRootsResult {
object Success : PutSaplingSubtreeRootsResult()
data class Failure(val failedAtHeight: BlockHeight, val exception: Throwable) : PutSaplingSubtreeRootsResult()
}

View File

@ -0,0 +1,12 @@
package cash.z.ecc.android.sdk.block.processor.model
import cash.z.ecc.android.sdk.internal.model.ScanRange
import cash.z.ecc.android.sdk.model.BlockHeight
/**
* Internal class for sharing suggested scan ranges action result.
*/
internal sealed class SuggestScanRangesResult {
data class Success(val ranges: List<ScanRange>) : SuggestScanRangesResult()
data class Failure(val failedAtHeight: BlockHeight, val exception: Throwable) : SuggestScanRangesResult()
}

View File

@ -0,0 +1,11 @@
package cash.z.ecc.android.sdk.block.processor.model
import cash.z.ecc.android.sdk.internal.model.BlockBatch
/**
* Common progress model class for sharing a batch synchronization stage result internally in the synchronization loop.
*/
internal data class SyncStageResult(
val batch: BlockBatch,
val stageResult: SyncingResult
)

View File

@ -0,0 +1,51 @@
package cash.z.ecc.android.sdk.block.processor.model
import cash.z.ecc.android.sdk.block.processor.CompactBlockProcessor
import cash.z.ecc.android.sdk.exception.CompactBlockProcessorException
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
import cash.z.ecc.android.sdk.model.BlockHeight
/**
* Internal class for the overall synchronization process result reporting.
*/
internal sealed class SyncingResult {
object AllSuccess : SyncingResult()
data class DownloadSuccess(val downloadedBlocks: List<JniBlockMeta>?) : SyncingResult() {
override fun toString(): String {
return "DownloadSuccess with ${downloadedBlocks?.size ?: "none"} blocks"
}
}
interface Failure {
val failedAtHeight: BlockHeight
val exception: CompactBlockProcessorException
fun toBlockProcessingResult(): CompactBlockProcessor.BlockProcessingResult =
CompactBlockProcessor.BlockProcessingResult.SyncFailure(
this.failedAtHeight,
this.exception
)
}
data class DownloadFailed(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
object ScanSuccess : SyncingResult()
data class ScanFailed(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
object DeleteSuccess : SyncingResult()
data class DeleteFailed(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
object EnhanceSuccess : SyncingResult()
data class EnhanceFailed(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
object UpdateBirthday : SyncingResult()
data class ContinuityError(
override val failedAtHeight: BlockHeight,
override val exception: CompactBlockProcessorException
) : Failure, SyncingResult()
}

View File

@ -0,0 +1,11 @@
package cash.z.ecc.android.sdk.block.processor.model
import cash.z.ecc.android.sdk.model.BlockHeight
/**
* Internal class for sharing update chain tip action result.
*/
internal sealed class UpdateChainTipResult {
data class Success(val height: BlockHeight) : UpdateChainTipResult()
data class Failure(val failedAtHeight: BlockHeight, val exception: Throwable) : UpdateChainTipResult()
}

View File

@ -0,0 +1,11 @@
package cash.z.ecc.android.sdk.block.processor.model
import cash.z.ecc.android.sdk.internal.model.ScanRange
/**
* Internal class for sharing verify suggested scan range action result.
*/
internal sealed class VerifySuggestedScanRange {
data class ShouldVerify(val scanRange: ScanRange) : VerifySuggestedScanRange()
object NoRangeToVerify : VerifySuggestedScanRange()
}

View File

@ -1,10 +1,8 @@
package cash.z.ecc.android.sdk.internal.ext package cash.z.ecc.android.sdk.internal.ext
import android.content.Context
import cash.z.ecc.android.sdk.ext.ZcashSdk.MAX_BACKOFF_INTERVAL import cash.z.ecc.android.sdk.ext.ZcashSdk.MAX_BACKOFF_INTERVAL
import cash.z.ecc.android.sdk.internal.Twig import cash.z.ecc.android.sdk.internal.Twig
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import java.io.File
import kotlin.math.pow import kotlin.math.pow
import kotlin.random.Random import kotlin.random.Random
@ -120,12 +118,3 @@ suspend inline fun retryWithBackoff(
} }
} }
} }
/**
* Return true if the given database already exists.
*
* @return true when the given database exists in the given context.
*/
internal fun dbExists(appContext: Context, dbFileName: String): Boolean {
return File(appContext.getDatabasePath(dbFileName).absolutePath).exists()
}

View File

@ -1,6 +1,5 @@
package cash.z.ecc.android.sdk.internal.model package cash.z.ecc.android.sdk.internal.model
import cash.z.ecc.android.sdk.internal.Twig
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
@ -11,7 +10,6 @@ internal data class ScanRange(
override fun toString() = "ScanRange(range=$range, priority=${getSuggestScanRangePriority()})" override fun toString() = "ScanRange(range=$range, priority=${getSuggestScanRangePriority()})"
internal fun getSuggestScanRangePriority(): SuggestScanRangePriority { internal fun getSuggestScanRangePriority(): SuggestScanRangePriority {
Twig.verbose { "Current suggested scan range priority: $priority" }
return SuggestScanRangePriority.values() return SuggestScanRangePriority.values()
.firstOrNull { it.priority == priority } ?: SuggestScanRangePriority.Scanned .firstOrNull { it.priority == priority } ?: SuggestScanRangePriority.Scanned
} }

View File

@ -1,6 +1,8 @@
package cash.z.ecc.android.sdk.ext package cash.z.ecc.android.sdk.ext
import cash.z.ecc.android.sdk.internal.ext.BLOCK_HEIGHT_DISCONTINUITY
import cash.z.ecc.android.sdk.internal.ext.PREV_HASH_MISMATCH import cash.z.ecc.android.sdk.internal.ext.PREV_HASH_MISMATCH
import cash.z.ecc.android.sdk.internal.ext.TREE_SIZE_MISMATCH
import cash.z.ecc.android.sdk.internal.ext.isScanContinuityError import cash.z.ecc.android.sdk.internal.ext.isScanContinuityError
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertFalse import kotlin.test.assertFalse
@ -10,15 +12,19 @@ class ExceptionExtTest {
@Test @Test
fun is_scan_continuity_error() { fun is_scan_continuity_error() {
assertTrue { assertTrue { RuntimeException(PREV_HASH_MISMATCH).isScanContinuityError() }
RuntimeException(PREV_HASH_MISMATCH).isScanContinuityError() assertTrue { RuntimeException(TREE_SIZE_MISMATCH).isScanContinuityError() }
} assertTrue { RuntimeException(BLOCK_HEIGHT_DISCONTINUITY).isScanContinuityError() }
assertTrue { RuntimeException(PREV_HASH_MISMATCH.lowercase()).isScanContinuityError() }
assertTrue { RuntimeException(PREV_HASH_MISMATCH.plus("Text")).isScanContinuityError() }
} }
@Test @Test
fun is_not_scan_continuity_error() { fun is_not_scan_continuity_error() {
assertFalse { assertFalse { RuntimeException("Text").isScanContinuityError() }
RuntimeException("Text").isScanContinuityError() assertFalse { RuntimeException("").isScanContinuityError() }
} assertFalse { RuntimeException(PREV_HASH_MISMATCH.drop(1)).isScanContinuityError() }
} }
} }

View File

@ -0,0 +1,17 @@
package cash.z.ecc.android.sdk.fixture
import cash.z.ecc.android.sdk.internal.model.ScanRange
import cash.z.ecc.android.sdk.internal.model.SuggestScanRangePriority
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.ZcashNetwork
object ScanRangeFixture {
internal val DEFAULT_CLOSED_RANGE =
ZcashNetwork.Testnet.saplingActivationHeight..ZcashNetwork.Testnet.saplingActivationHeight + 9
internal val DEFAULT_PRIORITY = SuggestScanRangePriority.Verify.priority
internal fun new(
range: ClosedRange<BlockHeight> = DEFAULT_CLOSED_RANGE,
priority: Long = DEFAULT_PRIORITY
) = ScanRange(range, priority)
}

View File

@ -0,0 +1,29 @@
package cash.z.ecc.android.sdk.internal.model
import cash.z.ecc.android.sdk.fixture.ScanRangeFixture
import cash.z.ecc.android.sdk.internal.ext.isNotEmpty
import cash.z.ecc.android.sdk.internal.ext.length
import cash.z.ecc.android.sdk.model.ZcashNetwork
import kotlin.test.Test
import kotlin.test.assertTrue
class ScanRangeTest {
@Test
fun get_suggest_scan_range_priority_test() {
val scanRange = ScanRangeFixture.new(
priority = SuggestScanRangePriority.Verify.priority
)
assertTrue {
scanRange.getSuggestScanRangePriority() == SuggestScanRangePriority.Verify
}
}
@Test
fun scan_range_boundaries_test() {
val scanRange = ScanRangeFixture.new(
range = ZcashNetwork.Testnet.saplingActivationHeight..ZcashNetwork.Testnet.saplingActivationHeight + 9
)
assertTrue { scanRange.range.isNotEmpty() }
assertTrue { scanRange.range.length() == 10L }
}
}