64 Custom apps should validate their expansion files and re-download if corrupt/missing - unit test custom download

This commit is contained in:
Sean Mac Gillicuddy 2019-11-05 16:17:14 +00:00
parent c05742d02a
commit f92664ca5f
22 changed files with 478 additions and 203 deletions

View File

@ -32,7 +32,7 @@ import kotlinx.android.synthetic.main.activity_language.language_recycler_view
import kotlinx.android.synthetic.main.activity_language.toolbar
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
import org.kiwix.kiwixmobile.kiwixActivityComponent
import org.kiwix.kiwixmobile.language.adapter.LanguageAdapter
import org.kiwix.kiwixmobile.language.adapter.LanguageDelegate.HeaderDelegate

View File

@ -27,7 +27,7 @@ import androidx.core.net.toFile
import androidx.core.net.toUri
import org.json.JSONArray
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.extensions.start
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.main.WebViewCallback

View File

@ -17,12 +17,9 @@
*/
package org.kiwix.kiwixmobile.zim_manager
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.Settings.System
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.widget.SearchView
@ -32,17 +29,15 @@ import kotlinx.android.synthetic.main.zim_manager.tabs
import kotlinx.android.synthetic.main.zim_manager.toolbar
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.extensions.start
import org.kiwix.kiwixmobile.core.extensions.startWithActionFrom
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.language.LanguageActivity
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.utils.Constants.TAG_KIWIX
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.startWithActionFrom
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.utils.LanguageUtils
import org.kiwix.kiwixmobile.local_file_transfer.LocalFileTransferActivity
import org.kiwix.kiwixmobile.kiwixActivityComponent
import java.io.File
import org.kiwix.kiwixmobile.language.LanguageActivity
import org.kiwix.kiwixmobile.local_file_transfer.LocalFileTransferActivity
import javax.inject.Inject
class ZimManageActivity : BaseActivity() {
@ -144,20 +139,6 @@ class ZimManageActivity : BaseActivity() {
return super.onOptionsItemSelected(item)
}
// Set zim file and return
fun finishResult(path: String?) {
if (path != null) {
val file = File(path)
val uri = Uri.fromFile(file)
Log.i(TAG_KIWIX, "Opening Zim File: $uri")
setResult(Activity.RESULT_OK, Intent().setData(uri))
finish()
} else {
setResult(Activity.RESULT_CANCELED)
finish()
}
}
companion object {
const val TAB_EXTRA = "TAB"
}

View File

@ -32,7 +32,7 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.BaseFragment
import org.kiwix.kiwixmobile.core.downloader.Downloader
import org.kiwix.kiwixmobile.core.downloader.model.DownloadItem
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
import org.kiwix.kiwixmobile.core.utils.DialogShower
import org.kiwix.kiwixmobile.core.utils.KiwixDialog.YesNoDialog.StopDownload
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil

View File

@ -37,8 +37,8 @@ import kotlinx.android.synthetic.main.zim_list.zimfilelist
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.BaseFragment
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.core.utils.Constants.REQUEST_STORAGE_PERMISSION
import org.kiwix.kiwixmobile.core.utils.LanguageUtils
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil

View File

@ -21,7 +21,7 @@ import android.app.Activity
import androidx.core.net.toUri
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.extensions.start
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.main.KiwixMainActivity

View File

@ -23,7 +23,7 @@ import android.view.ActionMode
import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.extensions.startActionMode
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.startActionMode
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection

View File

@ -39,9 +39,9 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.BaseFragment
import org.kiwix.kiwixmobile.core.downloader.Downloader
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
import org.kiwix.kiwixmobile.core.extensions.snack
import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.settings.StorageCalculator
import org.kiwix.kiwixmobile.core.utils.BookUtils

View File

@ -1 +0,0 @@
relaxUnitFun=true

View File

@ -1 +0,0 @@
junit.jupiter.testinstance.lifecycle.default=per_class

View File

@ -1,104 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<metalink xmlns="urn:ietf:params:xml:ns:metalink">
<generator>MirrorBrain/2.18.1</generator>
<origin dynamic="true">http://download.kiwix.org/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim.meta4</origin>
<published>2016-06-08T08:59:18Z</published>
<publisher>
<name>Kiwix project</name>
<url>http://www.kiwix.org</url>
</publisher>
<file name="wikipedia_af_all_nopic_2016-05.zim">
<size>63973123</size>
<!-- <mtime>1462622396</mtime> -->
<!-- internal id: 756021 -->
<hash type="md5">6f06866b61c4a921b57f28cfd4307220</hash>
<hash type="sha-1">8aac4c7f89e3cdd45b245695e19ecde5aac59593</hash>
<hash type="sha-256">83126775538cf588a85edb10db04d6e012321a2025278a08a084b258849b3a5c</hash>
<pieces length="1048576" type="sha-1">
<hash>f36815d904d4fd563aaef4ee6ef2600fb1fd70b2</hash>
<hash>cd34a8a85ed1dd275417ce224ff0cd8d9893d101</hash>
<hash>7702114faf59441937c9075ad695fad61cdb13f3</hash>
<hash>532553bfb04c870375a53368b17838e50b169ab8</hash>
<hash>fcda3eba296502965015ef3f07b05adeb4d95dfc</hash>
<hash>97af1a7b1327a6f13dfc63524c63077c4aaa1278</hash>
<hash>4c02b16d3d0cb1acc5b0eb02a46e3047245ca65c</hash>
<hash>163927a9e7405b20c0544d06243baecfdf3c13d3</hash>
<hash>1d4ccf5570fecefc93793aa1ebb73d95eb46cf03</hash>
<hash>a0e5200bde74ba58c7318e15a34d5f891fb36841</hash>
<hash>e862a19ce5a4066efdcc15bf3c74462292251171</hash>
<hash>1c964b3029b6eda5ddff6815bf5298ca9a4c4196</hash>
<hash>29f52efd444b496fc55c99541de62823605bdce7</hash>
<hash>d5ae5e610f0b2f102bc16d7eeea55cacf259dda8</hash>
<hash>b7e0102f182a84d76c3a8ca02d0e9319a320eed6</hash>
<hash>bd63b665f1ac5620226481bacbe4dee1d31d48ba</hash>
<hash>54393351f78e24c4ebb4dbfd22fcdfff279f39fb</hash>
<hash>8845733b1da2876ad81aa1f7596dfd0e49bcc293</hash>
<hash>94c2bb5e297a1c61bd1e4a3b47a2ffa8457dc9aa</hash>
<hash>b2c1229eb3ffa88c71f0584c9714b7cf2d7df091</hash>
<hash>d0978cf7aaba0320442673fc47f400e43df84a99</hash>
<hash>5b8e68db7197089a78e92eb77f1c63859d098b1b</hash>
<hash>d4ff1f37c101735c81a06ebd98e1c3be465e0314</hash>
<hash>e9399f2c1f883816d3007765443cd38a1b4c6f9f</hash>
<hash>b6fa56a9dc6893bd67deebd14e9f2ccd42d63355</hash>
<hash>73efed5e8fd6480fece063826d62241783126b63</hash>
<hash>b24ec4bd6f1f6ad2de9724275ac141c92692edd2</hash>
<hash>3d35dee6ee7e1add3e8f30fe601f4f74ed11e704</hash>
<hash>9af07844246402bc6e28f37f8eb8c0329033a874</hash>
<hash>1be6178907a740670d8e61f72ebc8d47860015ce</hash>
<hash>6e954cc098392cf7f515e07f4d666d8b0a5fe329</hash>
<hash>e9f09f347f07585338073fea96f8d009a5aa3e0d</hash>
<hash>a48801add4c60733a023bdcce5e62aacb33becf0</hash>
<hash>90ca6abeeb08fbc136f1d0234741ca6b506869bc</hash>
<hash>7c426983069ea0556168f9e7343426f79d233bc1</hash>
<hash>18e893707d86bb778f9be3957e2645502ae1469d</hash>
<hash>c7a984439cb7e7fbbc805ea9eb77bebcfbaa25a5</hash>
<hash>9614ca49b5611494e4aaa0a3ffd3e23f5bfae86a</hash>
<hash>d344c4cd8db6c0859eb00ba90cdeb1e12b9922c4</hash>
<hash>ef777338b04c4b638721e1a043c3ae433a885c82</hash>
<hash>04504c63113f7719f5ac13c8e30f1feec4648268</hash>
<hash>1411ad174573f8c2a9a2d33c33f77f776db40f35</hash>
<hash>bc771262c4abf687b49321c83fd4613dbdcc1aa3</hash>
<hash>6762c76f99280283fa73037a0358efe5fd903eee</hash>
<hash>8ba56ea08fe4c2f9e76ee8273ce9dad4c4bd977a</hash>
<hash>97d8246b1db42afb9048109f920811fb5e723e71</hash>
<hash>6c59c23baeb706f6902f5a8dc822bcf932721cdc</hash>
<hash>1dff0732d971bc25af5afa9768493f435e5ab5d0</hash>
<hash>133ff15a2992b0d5337eeddc62da839f86cee1e0</hash>
<hash>0590bc490e4f12782849b8b36b87e1b68becdb83</hash>
<hash>705e5c62c96c6b41a9cfea97796ebdcc981145a6</hash>
<hash>e03ba6cf2deba1dfc8d781e4b8d0c602e033eea4</hash>
<hash>a5c46e2093d7c138df4553bfc92604de91b8fe89</hash>
<hash>035f0bd63b253c88a5ef7efaea96b3ee664e6f91</hash>
<hash>3b4732487b464d6164d9e460d990dcf8de8b499c</hash>
<hash>3453494c2188f5bee8a86a7b3626129e61a8bb21</hash>
<hash>47a062cdd2f7024f6f6ebffe6ade5ca9474932ea</hash>
<hash>5c30eee23b5b75680958760472c5c39aaaab108c</hash>
<hash>c93ae5c2faf3f3a898e01f5c9aef1d57f2969990</hash>
<hash>debf6ed9bbfbc9db4505d89dc74f871049d5c918</hash>
<hash>3d0c0800396e5305ff1ddabba2371f817ec4839c</hash>
<hash>8055e515aa6e78f2810bbb0e0cd07330838b8920</hash>
</pieces>
<!-- Found 7 mirrors: 0 in the same network prefix, 0 in the same autonomous system,
0 handling this country, 0 in the same region, 5 elsewhere -->
<!-- Mirrors in the same network (unknown): -->
<!-- Mirrors in the same AS (unknown): -->
<!-- Mirrors which handle this country (unknown): -->
<!-- Mirrors in the same continent (unknown): -->
<!-- Mirrors in the rest of the world: -->
<url location="us" priority="1">http://ftpmirror.your.org/pub/kiwix/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim</url>
<url location="gb" priority="2">http://www.mirrorservice.org/sites/download.kiwix.org/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim</url>
<url location="us" priority="3">http://download.wikimedia.org/kiwix/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim</url>
<url location="de" priority="4">http://mirror.netcologne.de/kiwix/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim</url>
<url location="fr" priority="5">http://mirror3.kiwix.org/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim</url>
</file>
</metalink>

View File

@ -87,7 +87,7 @@ class AllProjectConfigurer {
lintOptions {
isAbortOnError = true
isCheckAllWarnings = true
isCheckAllWarnings = true
isWarningsAsErrors = true
ignore(
"SyntheticAccessor",
@ -102,7 +102,8 @@ class AllProjectConfigurer {
warning(
"UnknownNullness",
"SelectableText",
"IconDensities"
"IconDensities",
"ContentDescription"
)
baseline("${path}/lint-baseline.xml")
}
@ -121,6 +122,7 @@ class AllProjectConfigurer {
sourceSets {
getByName("test") {
java.srcDir("${target.rootDir}/core/src/sharedTestFunctions/java")
resources.srcDir("${target.rootDir}/core/src/test/resources")
}
}
}

View File

@ -30,55 +30,60 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import org.kiwix.kiwixmobile.core.Intents
fun Activity.startActionMode(
menuId: Int,
idsToClickActions: Map<Int, () -> Any>,
onDestroyAction: () -> Unit
): ActionMode? {
return startActionMode(object : Callback {
override fun onActionItemClicked(
mode: ActionMode,
item: MenuItem
) = idsToClickActions[item.itemId]?.let {
it()
mode.finish()
true
} ?: false
object ActivityExtensions {
override fun onCreateActionMode(
mode: ActionMode,
menu: Menu?
): Boolean {
mode.menuInflater
.inflate(menuId, menu)
return true
}
fun Activity.startActionMode(
menuId: Int,
idsToClickActions: Map<Int, () -> Any>,
onDestroyAction: () -> Unit
): ActionMode? {
return startActionMode(object : Callback {
override fun onActionItemClicked(
mode: ActionMode,
item: MenuItem
) = idsToClickActions[item.itemId]?.let {
it()
mode.finish()
true
} ?: false
override fun onPrepareActionMode(
mode: ActionMode?,
menu: Menu?
) = false
override fun onCreateActionMode(
mode: ActionMode,
menu: Menu?
): Boolean {
mode.menuInflater
.inflate(menuId, menu)
return true
}
override fun onDestroyActionMode(mode: ActionMode?) {
onDestroyAction()
}
})
override fun onPrepareActionMode(
mode: ActionMode?,
menu: Menu?
) = false
override fun onDestroyActionMode(mode: ActionMode?) {
onDestroyAction()
}
})
}
inline fun <reified T : Activity> Activity.start(
noinline intentFunc: (Intent.() -> Unit)? = null
) {
startActivity(
Intent(this, T::class.java).apply {
intentFunc?.invoke(this)
}
)
}
inline fun <reified T : Activity> Activity.startWithActionFrom() {
startActivity(Intents.internal(T::class.java))
}
inline fun <reified T : ViewModel> FragmentActivity.viewModel(
viewModelFactory: ViewModelProvider.Factory
) =
ViewModelProviders.of(this, viewModelFactory)
.get(T::class.java)
}
inline fun <reified T : Activity> Activity.start(noinline intentFunc: (Intent.() -> Unit)? = null) {
startActivity(
Intent(this, T::class.java).apply {
intentFunc?.invoke(this)
}
)
}
inline fun <reified T : Activity> Activity.startWithActionFrom() {
startActivity(Intents.internal(T::class.java))
}
inline fun <reified T : ViewModel> FragmentActivity.viewModel(
viewModelFactory: ViewModelProvider.Factory
) =
ViewModelProviders.of(this, viewModelFactory)
.get(T::class.java)

View File

@ -9,14 +9,12 @@
</issue>
<issue
id="SdCardPath"
message="Do not hardcode &quot;`/data/`&quot;; use `Context.getFilesDir().getPath()` instead"
errorLine1=" &quot;/data/data/%s/lib/%s&quot;, packageName,"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
id="ObsoleteLintCustomCheck"
message="Lint found an issue registry (`butterknife.lint.LintRegistry`) which is older than the current API level; these checks may not work correctly.&#xA;&#xA;Recompile the checks against the latest version. Custom check API version is 2 (3.2), current lint API level is 5 (3.5+)"
includedVariants="customexampleDebug"
excludedVariants="customexampleRelease">
<location
file="src\main\java\org\kiwix\kiwixmobile\custom\main\CustomMainActivity.kt"
line="109"
column="12"/>
file="..\..\..\.gradle\caches\transforms-2\files-2.1\6dca90c1110a5ba8556122fc8f89395f\butterknife-runtime-10.1.0\jars\lint.jar"/>
</issue>
<issue
@ -58,9 +56,17 @@
<issue
id="UnusedResources"
message="The resource `R.drawable.kiwix_icon_with_title` appears to be unused">
message="The resource `R.mipmap.kiwix_icon` appears to be unused">
<location
file="src\customexample\res\drawable\kiwix_icon_with_title.png"/>
file="src\customexample\res\mipmap-hdpi\kiwix_icon.png"/>
<location
file="src\customexample\res\mipmap-mdpi\kiwix_icon.png"/>
<location
file="src\customexample\res\mipmap-xhdpi\kiwix_icon.png"/>
<location
file="src\customexample\res\mipmap-xxhdpi\kiwix_icon.png"/>
<location
file="src\customexample\res\mipmap-xxxhdpi\kiwix_icon.png"/>
</issue>
<issue
@ -74,6 +80,13 @@
column="11"/>
</issue>
<issue
id="UnusedResources"
message="The resource `R.raw.wikipedia_fr_test_2017_07` appears to be unused">
<location
file="src\customexample\res\raw\wikipedia_fr_test_2017_07.zim"/>
</issue>
<issue
id="IconDipSize"
message="The image `ic_kiwix_widget.png` varies significantly in its density-independent (dip) size across the various density versions: drawable-hdpi\\ic_kiwix_widget.png: 48x48 dp (72x72 px), drawable-mdpi\\ic_kiwix_widget.png: 72x72 dp (72x72 px), drawable-xhdpi\\ic_kiwix_widget.png: 48x48 dp (96x96 px), drawable-xxhdpi\\ic_kiwix_widget.png: 48x48 dp (144x144 px), drawable-xxxhdpi\\ic_kiwix_widget.png: 48x48 dp (192x192 px)">
@ -156,6 +169,39 @@
file="src\customexample\res\drawable-xxxhdpi\ic_kiwix_widget.png"/>
</issue>
<issue
id="SelectableText"
message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
errorLine1=" &lt;TextView"
errorLine2=" ~~~~~~~~">
<location
file="src\main\res\layout\layout_custom_download_error.xml"
line="10"
column="4"/>
</issue>
<issue
id="SelectableText"
message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
errorLine1=" &lt;TextView"
errorLine2=" ~~~~~~~~">
<location
file="src\main\res\layout\layout_custom_download_in_progress.xml"
line="8"
column="4"/>
</issue>
<issue
id="SelectableText"
message="Consider making the text value selectable by specifying `android:textIsSelectable=&quot;true&quot;`"
errorLine1=" &lt;TextView"
errorLine2=" ~~~~~~~~">
<location
file="src\main\res\layout\layout_custom_download_in_progress.xml"
line="28"
column="4"/>
</issue>
<issue
id="GoogleAppIndexingApiWarning"
message="Missing support for Firebase App Indexing API"
@ -167,4 +213,26 @@
column="7"/>
</issue>
<issue
id="ContentDescription"
message="Missing `contentDescription` attribute on image"
errorLine1=" &lt;ImageView"
errorLine2=" ~~~~~~~~~">
<location
file="src\main\res\layout\activity_custom_download.xml"
line="9"
column="4"/>
</issue>
<issue
id="UnknownNullness"
message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://android.github.io/kotlin-guides/interop.html#nullability-annotations"
errorLine1=" public CustomViewModelFactory(Map&lt;Class&lt;? extends ViewModel>, Provider&lt;ViewModel>> creators) {"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="src\main\java\org\kiwix\kiwixmobile\custom\CustomViewModelFactory.java"
line="32"
column="33"/>
</issue>
</issues>

View File

@ -31,8 +31,8 @@ import kotlinx.android.synthetic.main.layout_custom_download_in_progress.cd_prog
import kotlinx.android.synthetic.main.layout_custom_download_required.cd_download_button
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.downloader.model.DownloadItem
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
import org.kiwix.kiwixmobile.core.extensions.setDistinctDisplayedChild
import org.kiwix.kiwixmobile.core.extensions.viewModel
import org.kiwix.kiwixmobile.custom.R
import org.kiwix.kiwixmobile.custom.customActivityComponent
import org.kiwix.kiwixmobile.custom.download.Action.ClickedDownload

View File

@ -41,7 +41,8 @@ import javax.inject.Inject
class CustomDownloadViewModel @Inject constructor(
downloadDao: FetchDownloadDao,
setPreferredStorageWithMostSpace: SetPreferredStorageWithMostSpace,
private val downloadCustom: DownloadCustom
private val downloadCustom: DownloadCustom,
private val finishAndStartMain: FinishAndStartMain
) : ViewModel() {
val state = MutableLiveData<State>().apply { value = DownloadRequired }
@ -90,7 +91,7 @@ class CustomDownloadViewModel @Inject constructor(
else
DownloadInProgress(action.downloads)
else
DownloadComplete.also { _effects.offer(FinishAndStartMain()) }
DownloadComplete.also { _effects.offer(finishAndStartMain) }
DownloadComplete -> state
}
}

View File

@ -20,10 +20,11 @@ package org.kiwix.kiwixmobile.custom.download.effects
import android.app.Activity
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.extensions.start
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
import org.kiwix.kiwixmobile.custom.main.CustomMainActivity
import javax.inject.Inject
class FinishAndStartMain() : SideEffect<Unit> {
class FinishAndStartMain @Inject constructor() : SideEffect<Unit> {
override fun invokeWith(activity: Activity) {
activity.finish()
activity.start<CustomMainActivity>()

View File

@ -24,7 +24,7 @@ import android.util.Log
import android.view.Menu
import org.kiwix.kiwixmobile.core.CoreApp
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.extensions.start
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.main.WebViewCallback
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer

View File

@ -0,0 +1,176 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.custom.download
import com.jraska.livedata.test
import com.tonyodev.fetch2.Error.NONE
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.mockk
import io.reactivex.processors.PublishProcessor
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.downloader.model.DownloadState
import org.kiwix.kiwixmobile.core.downloader.model.DownloadState.Failed
import org.kiwix.kiwixmobile.custom.download.Action.ClickedDownload
import org.kiwix.kiwixmobile.custom.download.Action.ClickedRetry
import org.kiwix.kiwixmobile.custom.download.Action.DatabaseEmission
import org.kiwix.kiwixmobile.custom.download.State.DownloadComplete
import org.kiwix.kiwixmobile.custom.download.State.DownloadFailed
import org.kiwix.kiwixmobile.custom.download.State.DownloadInProgress
import org.kiwix.kiwixmobile.custom.download.State.DownloadRequired
import org.kiwix.kiwixmobile.custom.download.effects.DownloadCustom
import org.kiwix.kiwixmobile.custom.download.effects.FinishAndStartMain
import org.kiwix.kiwixmobile.custom.download.effects.SetPreferredStorageWithMostSpace
import org.kiwix.sharedFunctions.InstantExecutorExtension
import org.kiwix.sharedFunctions.downloadItem
@ExtendWith(InstantExecutorExtension::class)
internal class CustomDownloadViewModelTest {
private val fetchDownloadDao: FetchDownloadDao = mockk()
private val setPreferredStorageWithMostSpace: SetPreferredStorageWithMostSpace = mockk()
private val downloadCustom: DownloadCustom = mockk()
private val finishAndStartMain: FinishAndStartMain = mockk()
private val downloads: PublishProcessor<List<DownloadModel>> = PublishProcessor.create()
private lateinit var customDownloadViewModel: CustomDownloadViewModel
@BeforeEach
internal fun setUp() {
clearAllMocks()
every { fetchDownloadDao.downloads() } returns downloads
customDownloadViewModel = CustomDownloadViewModel(
fetchDownloadDao,
setPreferredStorageWithMostSpace,
downloadCustom,
finishAndStartMain
)
}
@Test
internal fun `effects emits SetPreferred on Subscribe`() {
customDownloadViewModel.effects.test().assertValue(setPreferredStorageWithMostSpace)
}
@Test
internal fun `initial State is DownloadRequired`() {
customDownloadViewModel.state.test().assertValue(State.DownloadRequired)
}
@Nested
inner class DownloadEmissions {
@Test
internal fun `Emission with data moves state from Required to InProgress`() {
assertStateTransition(
DownloadRequired,
DatabaseEmission(listOf(downloadItem())),
State.DownloadInProgress(listOf(downloadItem()))
)
}
@Test
internal fun `Emission without data moves state from Required to Required`() {
assertStateTransition(DownloadRequired, DatabaseEmission(listOf()), DownloadRequired)
}
@Test
internal fun `Emission with data moves state from Failed to InProgress`() {
assertStateTransition(
DownloadFailed(DownloadState.Pending),
DatabaseEmission(listOf(downloadItem())),
State.DownloadInProgress(listOf(downloadItem()))
)
}
@Test
internal fun `Emission without data moves state from Failed to Failed`() {
assertStateTransition(
DownloadFailed(DownloadState.Pending),
DatabaseEmission(listOf()),
DownloadFailed(DownloadState.Pending)
)
}
@Test
internal fun `Emission with data+failure moves state from InProgress to Failed`() {
assertStateTransition(
DownloadInProgress(listOf()),
DatabaseEmission(listOf(downloadItem(state = Failed(NONE)))),
DownloadFailed(Failed(NONE))
)
}
@Test
internal fun `Emission with data moves state from InProgress to InProgress`() {
assertStateTransition(
DownloadInProgress(listOf(downloadItem(downloadId = 1L))),
DatabaseEmission(listOf(downloadItem(downloadId = 2L))),
DownloadInProgress(listOf(downloadItem(downloadId = 2L)))
)
}
@Test
internal fun `Emission without data moves state from InProgress to Complete`() {
val sideEffects = customDownloadViewModel.effects.test()
assertStateTransition(
DownloadInProgress(listOf()),
DatabaseEmission(listOf()),
DownloadComplete
)
sideEffects.assertValues(setPreferredStorageWithMostSpace, finishAndStartMain)
}
@Test
internal fun `Any emission does not change state from Complete`() {
assertStateTransition(
DownloadComplete,
DatabaseEmission(listOf(downloadItem())),
DownloadComplete
)
}
private fun assertStateTransition(
initialState: State,
action: DatabaseEmission,
endState: State
) {
customDownloadViewModel.state.value = initialState
customDownloadViewModel.actions.offer(action)
customDownloadViewModel.state.test().assertValue(endState)
}
}
@Test
internal fun `clicking Retry triggers DownloadCustom`() {
val sideEffects = customDownloadViewModel.effects.test()
customDownloadViewModel.actions.offer(ClickedRetry)
sideEffects.assertValues(setPreferredStorageWithMostSpace, downloadCustom)
}
@Test
internal fun `clicking Download triggers DownloadCustom`() {
val sideEffects = customDownloadViewModel.effects.test()
customDownloadViewModel.actions.offer(ClickedDownload)
sideEffects.assertValues(setPreferredStorageWithMostSpace, downloadCustom)
}
}

View File

@ -0,0 +1,53 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.custom.download.effects
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Test
import org.kiwix.kiwixmobile.core.downloader.Downloader
import org.kiwix.sharedFunctions.book
internal class DownloadCustomTest {
@Test
fun `invokeWith queues download with ZimUrl`() {
val downloader = mockk<Downloader>()
DownloadCustom(downloader).invokeWith(mockk())
verify {
downloader.download(expectedBook())
}
}
private fun expectedBook() = book(
"custom",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
""
)
}

View File

@ -0,0 +1,40 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.custom.download.effects
import android.app.Activity
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Test
internal class FinishAndStartMainTest {
@Test
fun `invokeWith finishes activity and starts main`() {
val activity = mockk<Activity>()
// Inline functions cannot be mocked
// mockkObject(ActivityExtensions)
// every { start<CustomMainActivity>(null) } just Runs issues with inline extension functions
FinishAndStartMain().invokeWith(activity)
verify {
activity.finish()
activity.startActivity(any())
}
}
}

View File

@ -0,0 +1,54 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.custom.download.effects
import android.app.Activity
import androidx.core.content.ContextCompat
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.verify
import org.junit.jupiter.api.Test
import org.kiwix.kiwixmobile.core.settings.StorageCalculator
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import java.io.File
internal class SetPreferredStorageWithMostSpaceTest {
@Test
fun `invokeWith sets the storage with the most space as preferred`() {
val storageCalculator = mockk<StorageCalculator>()
val sharedPreferenceUtil = mockk<SharedPreferenceUtil>()
val activity = mockk<Activity>()
mockkStatic(ContextCompat::class)
val directoryWithMoreStorage = mockk<File>()
val directoryWithLessStorage = mockk<File>()
every { ContextCompat.getExternalFilesDirs(activity, null) } returns arrayOf(
directoryWithMoreStorage, null, directoryWithLessStorage
)
every { storageCalculator.availableBytes(directoryWithMoreStorage) } returns 1
every { storageCalculator.availableBytes(directoryWithLessStorage) } returns 0
val expectedStorage = "expectedStorage"
every { directoryWithMoreStorage.path } returns expectedStorage
SetPreferredStorageWithMostSpace(storageCalculator, sharedPreferenceUtil).invokeWith(activity)
verify {
sharedPreferenceUtil.putPrefStorage(expectedStorage)
}
}
}