[#270] Add regression tests for assets
This commit is contained in:
parent
f9d95bca91
commit
a58a235d2c
|
@ -0,0 +1,108 @@
|
|||
package cash.z.ecc.android.sdk
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import cash.z.ecc.android.sdk.tool.WalletBirthdayTool
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import org.json.JSONObject
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AssetTest {
|
||||
@Test
|
||||
@SmallTest
|
||||
fun birthday_height_from_filename() {
|
||||
assertEquals(123, WalletBirthdayTool.birthdayHeight("123.json"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun validate_mainnet_assets() {
|
||||
val network = ZcashNetwork.Mainnet
|
||||
val assets = listAssets(network)
|
||||
|
||||
assertFilesExist(assets)
|
||||
assertFilenames(assets)
|
||||
assertFileContents(network, assets)
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
fun validate_testnet_assets() {
|
||||
val network = ZcashNetwork.Testnet
|
||||
val assets = listAssets(network)
|
||||
|
||||
assertFilesExist(assets)
|
||||
assertFilenames(assets)
|
||||
assertFileContents(network, assets)
|
||||
}
|
||||
|
||||
private fun assertFilesExist(files: Array<String>?) {
|
||||
assertFalse(files.isNullOrEmpty())
|
||||
}
|
||||
|
||||
private fun assertFilenames(files: Array<String>?) {
|
||||
files?.forEach {
|
||||
val split = it.split('.')
|
||||
assertEquals(2, split.size)
|
||||
|
||||
val intString = split.first()
|
||||
val extensionString = split.last()
|
||||
|
||||
// Will throw exception if cannot be parsed
|
||||
intString.toInt()
|
||||
|
||||
assertEquals("json", extensionString)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertFileContents(network: ZcashNetwork, files: Array<String>?) {
|
||||
files?.map { filename ->
|
||||
val filePath = "${WalletBirthdayTool.birthdayDirectory(network)}/$filename"
|
||||
ApplicationProvider.getApplicationContext<Context>().assets.open(filePath)
|
||||
.use { inputSteam ->
|
||||
inputSteam.bufferedReader().use { bufferedReader ->
|
||||
val slurped = bufferedReader.readText()
|
||||
|
||||
JsonFile(JSONObject(slurped), filename)
|
||||
}
|
||||
}
|
||||
}?.forEach {
|
||||
val jsonObject = it.jsonObject
|
||||
assertTrue(jsonObject.has("network"))
|
||||
assertTrue(jsonObject.has("height"))
|
||||
assertTrue(jsonObject.has("hash"))
|
||||
assertTrue(jsonObject.has("time"))
|
||||
assertTrue(jsonObject.has("tree"))
|
||||
|
||||
val expectedNetworkName = when (network) {
|
||||
ZcashNetwork.Mainnet -> "main"
|
||||
ZcashNetwork.Testnet -> "test"
|
||||
}
|
||||
assertEquals("File: ${it.filename}", expectedNetworkName, jsonObject.getString("network"))
|
||||
|
||||
assertEquals(
|
||||
"File: ${it.filename}",
|
||||
WalletBirthdayTool.birthdayHeight(it.filename),
|
||||
jsonObject.getInt("height")
|
||||
)
|
||||
|
||||
// In the future, additional validation of the JSON can be added
|
||||
}
|
||||
}
|
||||
|
||||
private data class JsonFile(val jsonObject: JSONObject, val filename: String)
|
||||
|
||||
companion object {
|
||||
fun listAssets(network: ZcashNetwork) = WalletBirthdayTool.listBirthdayDirectoryContents(
|
||||
ApplicationProvider.getApplicationContext<Context>(),
|
||||
WalletBirthdayTool.birthdayDirectory(network)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,15 +1,16 @@
|
|||
package cash.z.ecc.android.sdk.tool
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import cash.z.ecc.android.sdk.exception.BirthdayException
|
||||
import cash.z.ecc.android.sdk.ext.twig
|
||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.stream.JsonReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.util.Arrays
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Tool for loading checkpoints for the wallet, based on the height at which the wallet was born.
|
||||
|
@ -17,7 +18,7 @@ import java.util.Locale
|
|||
* @param appContext needed for loading checkpoints from the app's assets directory.
|
||||
*/
|
||||
class WalletBirthdayTool(appContext: Context) {
|
||||
val context = appContext.applicationContext
|
||||
private val context = appContext.applicationContext
|
||||
|
||||
/**
|
||||
* Load the nearest checkpoint to the given birthday height. If null is given, then this
|
||||
|
@ -51,13 +52,23 @@ class WalletBirthdayTool(appContext: Context) {
|
|||
)
|
||||
}
|
||||
|
||||
// TODO: This method performs disk IO; convert to suspending function
|
||||
// Converting this to suspending will then propagate
|
||||
@Throws(IOException::class)
|
||||
internal fun listBirthdayDirectoryContents(context: Context, directory: String) =
|
||||
context.assets.list(directory)
|
||||
|
||||
/**
|
||||
* Returns the directory within the assets folder where birthday data
|
||||
* (i.e. sapling trees for a given height) can be found.
|
||||
*/
|
||||
private fun birthdayDirectory(network: ZcashNetwork): String {
|
||||
return "saplingtree/${network.networkName.toLowerCase(Locale.US)}"
|
||||
}
|
||||
@VisibleForTesting
|
||||
internal fun birthdayDirectory(network: ZcashNetwork) =
|
||||
"saplingtree/${network.networkName.lowercase()}"
|
||||
|
||||
internal fun birthdayHeight(fileName: String) = fileName.split('.').first().toInt()
|
||||
|
||||
private fun Array<String>.sortDescending() = apply { sortByDescending { birthdayHeight(it) } }
|
||||
|
||||
/**
|
||||
* Load the given birthday file from the assets of the given context. When no height is
|
||||
|
@ -76,16 +87,7 @@ class WalletBirthdayTool(appContext: Context) {
|
|||
): WalletBirthday {
|
||||
twig("loading birthday from assets: $birthdayHeight")
|
||||
val directory = birthdayDirectory(network)
|
||||
val treeFiles =
|
||||
context.assets.list(directory)?.apply {
|
||||
sortByDescending { fileName ->
|
||||
try {
|
||||
fileName.split('.').first().toInt()
|
||||
} catch (t: Throwable) {
|
||||
network.saplingActivationHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
val treeFiles = listBirthdayDirectoryContents(context, directory)?.sortDescending()
|
||||
if (treeFiles.isNullOrEmpty()) throw BirthdayException.MissingBirthdayFilesException(
|
||||
directory
|
||||
)
|
||||
|
@ -94,7 +96,7 @@ class WalletBirthdayTool(appContext: Context) {
|
|||
try {
|
||||
file = if (birthdayHeight == null) treeFiles.first() else {
|
||||
treeFiles.first {
|
||||
it.split(".").first().toInt() <= birthdayHeight
|
||||
birthdayHeight(it) <= birthdayHeight
|
||||
}
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
|
|
Loading…
Reference in New Issue