[#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
|
package cash.z.ecc.android.sdk.tool
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import cash.z.ecc.android.sdk.exception.BirthdayException
|
import cash.z.ecc.android.sdk.exception.BirthdayException
|
||||||
import cash.z.ecc.android.sdk.ext.twig
|
import cash.z.ecc.android.sdk.ext.twig
|
||||||
import cash.z.ecc.android.sdk.type.WalletBirthday
|
import cash.z.ecc.android.sdk.type.WalletBirthday
|
||||||
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
import cash.z.ecc.android.sdk.type.ZcashNetwork
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
|
import java.io.IOException
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.util.Arrays
|
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.
|
* 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.
|
* @param appContext needed for loading checkpoints from the app's assets directory.
|
||||||
*/
|
*/
|
||||||
class WalletBirthdayTool(appContext: Context) {
|
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
|
* 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
|
* Returns the directory within the assets folder where birthday data
|
||||||
* (i.e. sapling trees for a given height) can be found.
|
* (i.e. sapling trees for a given height) can be found.
|
||||||
*/
|
*/
|
||||||
private fun birthdayDirectory(network: ZcashNetwork): String {
|
@VisibleForTesting
|
||||||
return "saplingtree/${network.networkName.toLowerCase(Locale.US)}"
|
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
|
* 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 {
|
): WalletBirthday {
|
||||||
twig("loading birthday from assets: $birthdayHeight")
|
twig("loading birthday from assets: $birthdayHeight")
|
||||||
val directory = birthdayDirectory(network)
|
val directory = birthdayDirectory(network)
|
||||||
val treeFiles =
|
val treeFiles = listBirthdayDirectoryContents(context, directory)?.sortDescending()
|
||||||
context.assets.list(directory)?.apply {
|
|
||||||
sortByDescending { fileName ->
|
|
||||||
try {
|
|
||||||
fileName.split('.').first().toInt()
|
|
||||||
} catch (t: Throwable) {
|
|
||||||
network.saplingActivationHeight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (treeFiles.isNullOrEmpty()) throw BirthdayException.MissingBirthdayFilesException(
|
if (treeFiles.isNullOrEmpty()) throw BirthdayException.MissingBirthdayFilesException(
|
||||||
directory
|
directory
|
||||||
)
|
)
|
||||||
|
@ -94,7 +96,7 @@ class WalletBirthdayTool(appContext: Context) {
|
||||||
try {
|
try {
|
||||||
file = if (birthdayHeight == null) treeFiles.first() else {
|
file = if (birthdayHeight == null) treeFiles.first() else {
|
||||||
treeFiles.first {
|
treeFiles.first {
|
||||||
it.split(".").first().toInt() <= birthdayHeight
|
birthdayHeight(it) <= birthdayHeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
|
|
Loading…
Reference in New Issue