Fixed: Compilation issues in unit test cases. The issue was in Gradle itself, which was preventing us from merging or excluding duplicate META-INF files.

* Upgraded Gradle to `8.11.0-alpha03` for better support for Android 16.
* Upgraded the Gradle wrapper to `8.13`, the latest version.
* Fixed: Some deprecated methods in the custom app's Gradle. The new methods for registering tasks are better than the previous ones, which is why Gradle deprecated the older methods.
* Refactored the `createDynamically` task for custom apps according to new gradle.
* Fixed: Some lint and detekt issues.
This commit is contained in:
MohitMaliFtechiz 2025-03-31 15:27:31 +05:30
parent 993c13f85e
commit 05e231579b
14 changed files with 50 additions and 46 deletions

View File

@ -1,3 +1,4 @@
import com.android.build.gradle.internal.tasks.factory.dependsOn
import com.slack.keeper.optInToKeeper import com.slack.keeper.optInToKeeper
import org.w3c.dom.Element import org.w3c.dom.Element
import plugin.KiwixConfigurationPlugin import plugin.KiwixConfigurationPlugin
@ -130,7 +131,7 @@ dependencies {
androidTestImplementation(Libs.leakcanary_android_instrumentation) androidTestImplementation(Libs.leakcanary_android_instrumentation)
testImplementation(Libs.kotlinx_coroutines_test) testImplementation(Libs.kotlinx_coroutines_test)
} }
task("generateVersionCodeAndName") { tasks.register("generateVersionCodeAndName") {
val file = File("VERSION_INFO") val file = File("VERSION_INFO")
if (!file.exists()) file.createNewFile() if (!file.exists()) file.createNewFile()
file.printWriter().use { file.printWriter().use {
@ -138,7 +139,7 @@ task("generateVersionCodeAndName") {
} }
} }
task("renameTarakFile") { tasks.register("renameTarakFile") {
val taraskFile = File("$rootDir/core/src/main/res/values-b+be+tarask/strings.xml") val taraskFile = File("$rootDir/core/src/main/res/values-b+be+tarask/strings.xml")
val mainStringsFile = File("$rootDir/core/src/main/res/values/strings.xml") val mainStringsFile = File("$rootDir/core/src/main/res/values/strings.xml")
@ -214,6 +215,10 @@ fun elementToString(element: Element): String {
return result.writer.toString() return result.writer.toString()
} }
tasks.build { gradle.projectsEvaluated {
dependsOn("renameTarakFile") tasks.forEach { task ->
if (task.name != "renameTarakFile") {
task.dependsOn("renameTarakFile")
}
}
} }

View File

@ -1,3 +1,5 @@
import org.gradle.kotlin.dsl.register
buildscript { buildscript {
repositories { repositories {
google() google()
@ -28,6 +30,6 @@ allprojects {
} }
} }
tasks.create<Delete>("clean") { tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory) delete(rootProject.layout.buildDirectory)
} }

View File

@ -11,7 +11,7 @@ repositories {
} }
dependencies { dependencies {
implementation("com.android.tools.build:gradle:8.10.0-alpha08") implementation("com.android.tools.build:gradle:8.11.0-alpha03")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0") implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
implementation("com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.0.0-1.0.24") implementation("com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.0.0-1.0.24")
implementation("org.jacoco:org.jacoco.core:0.8.12") implementation("org.jacoco:org.jacoco.core:0.8.12")

View File

@ -48,7 +48,7 @@ object Versions {
const val android_arch_lifecycle_extensions: String = "1.1.1" const val android_arch_lifecycle_extensions: String = "1.1.1"
const val com_android_tools_build_gradle: String = "8.10.0-alpha08" const val com_android_tools_build_gradle: String = "8.11.0-alpha03"
const val de_fayard_buildsrcversions_gradle_plugin: String = "0.7.0" const val de_fayard_buildsrcversions_gradle_plugin: String = "0.7.0"

View File

@ -24,7 +24,7 @@ import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser import org.json.simple.parser.JSONParser
import java.io.File import java.io.File
typealias ProductFlavors = NamedDomainObjectContainer<ProductFlavor> typealias ProductFlavors = NamedDomainObjectContainer<out ProductFlavor>
object CustomApps { object CustomApps {

View File

@ -91,7 +91,9 @@ internal class NewBookDaoTest {
} }
} }
private fun expectEmissionOfExistingAndNotExistingBook(isInTrashFolder: Boolean = false): Pair<BookOnDiskEntity, BookOnDiskEntity> { private fun expectEmissionOfExistingAndNotExistingBook(
isInTrashFolder: Boolean = false
): Pair<BookOnDiskEntity, BookOnDiskEntity> {
val query: Query<BookOnDiskEntity> = mockk() val query: Query<BookOnDiskEntity> = mockk()
every { box.query().build() } returns query every { box.query().build() } returns query
val zimReaderSourceThatExists = mockk<ZimReaderSource>() val zimReaderSourceThatExists = mockk<ZimReaderSource>()

View File

@ -18,17 +18,17 @@
package org.kiwix.kiwixmobile.core.dao package org.kiwix.kiwixmobile.core.dao
import org.junit.jupiter.api.Test
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import io.objectbox.Box import io.objectbox.Box
import io.objectbox.query.Query import io.objectbox.query.Query
import io.objectbox.query.QueryBuilder import io.objectbox.query.QueryBuilder
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_ import org.junit.jupiter.api.Test
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity
import org.kiwix.kiwixmobile.core.page.bookmark import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_
import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.page.adapter.Page
import org.kiwix.kiwixmobile.core.page.bookmark
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem
import org.kiwix.kiwixmobile.core.reader.ZimFileReader import org.kiwix.kiwixmobile.core.reader.ZimFileReader
@ -69,7 +69,6 @@ internal class NewBookmarksDaoTest {
every { bookmarkItem.zimName } returns "" every { bookmarkItem.zimName } returns ""
every { bookmarkItem.databaseId } returns 0L every { bookmarkItem.databaseId } returns 0L
newBookmarksDao.getCurrentZimBookmarksUrl(zimFileReader) newBookmarksDao.getCurrentZimBookmarksUrl(zimFileReader)
val bookmarkEntity: BookmarkEntity = mockk()
every { every {
query.property(BookmarkEntity_.bookmarkUrl).findStrings().toList().distinct() query.property(BookmarkEntity_.bookmarkUrl).findStrings().toList().distinct()
} returns listOf("") } returns listOf("")

View File

@ -27,13 +27,12 @@ class MetaLinkNetworkEntityTest {
@Throws(Exception::class) @Throws(Exception::class)
fun testDeserialize() { fun testDeserialize() {
val serializer = Persister() val serializer = Persister()
val result = val result = serializer.read(
serializer.read( MetaLinkNetworkEntity::class.java,
MetaLinkNetworkEntity::class.java, MetaLinkNetworkEntityTest::class.java.classLoader!!.getResourceAsStream(
MetaLinkNetworkEntityTest::class.java.classLoader!!.getResourceAsStream( "wikipedia_af_all_nopic_2016-05.zim.meta4"
"wikipedia_af_all_nopic_2016-05.zim.meta4"
)
) )
)
result?.urls?.let { result?.urls?.let {
MetaLinkNetworkEntityUrlAssert(it).hasItems( MetaLinkNetworkEntityUrlAssert(it).hasItems(
listOf( listOf(
@ -68,9 +67,7 @@ class MetaLinkNetworkEntityTest {
} }
// Basic file attributes // Basic file attributes
assertThat(result.file?.name).isEqualTo("wikipedia_af_all_nopic_2016-05.zim") assertThat(result.file?.name).isEqualTo("wikipedia_af_all_nopic_2016-05.zim")
assertThat(result.file?.size).isEqualTo(63973123L) assertThat(result.file?.size).isEqualTo(63973123L)
// File hashes // File hashes
assertThat(result.file?.getHash("md5")).isEqualTo("6f06866b61c4a921b57f28cfd4307220") assertThat(result.file?.getHash("md5")).isEqualTo("6f06866b61c4a921b57f28cfd4307220")
assertThat( assertThat(
@ -103,21 +100,17 @@ class MetaLinkNetworkEntityTest {
*/ */
class MetaLinkNetworkEntityUrlAssert( class MetaLinkNetworkEntityUrlAssert(
actual: List<MetaLinkNetworkEntity.Url> actual: List<MetaLinkNetworkEntity.Url>
) : ) : AbstractAssert<MetaLinkNetworkEntityUrlAssert, List<MetaLinkNetworkEntity.Url>>(
AbstractAssert<MetaLinkNetworkEntityUrlAssert, List<MetaLinkNetworkEntity.Url>>( actual,
actual, MetaLinkNetworkEntityUrlAssert::class.java
MetaLinkNetworkEntityUrlAssert::class.java ) {
) {
private fun <S, T> intersectionWith( private fun <S, T> intersectionWith(
first: List<S>, first: List<S>,
second: List<T>, second: List<T>,
function: (S, T) -> Boolean function: (S, T) -> Boolean
): Boolean { ): Boolean {
val filtered = first.filter { a -> second.any { b -> function(a, b) } } val filtered = first.filter { a -> second.any { b -> function(a, b) } }
if (filtered.isNotEmpty()) { return filtered.isNotEmpty()
return true
}
return false
} }
fun hasItems(items: List<DummyUrl>): Boolean { fun hasItems(items: List<DummyUrl>): Boolean {

View File

@ -43,8 +43,9 @@ internal class SearchStateTest {
val estimatedMatches = 100 val estimatedMatches = 100
every { suggestionSearchWrapper.estimatedMatches } returns estimatedMatches.toLong() every { suggestionSearchWrapper.estimatedMatches } returns estimatedMatches.toLong()
// Settings list to hasNext() to ensure it returns true only for the first call. // Settings list to hasNext() to ensure it returns true only for the first call.
// Otherwise, if we do not set this, the method will always return true when checking if the iterator has a next value, // Otherwise, if we do not set this, the method will always return true when
// causing our test case to get stuck in an infinite loop due to this explicit setting. // checking if the iterator has a next value, causing our test case to get stuck
// in an infinite loop due to this explicit setting.
every { searchIteratorWrapper.hasNext() } returnsMany listOf(true, false) every { searchIteratorWrapper.hasNext() } returnsMany listOf(true, false)
every { searchIteratorWrapper.next() } returns entryWrapper every { searchIteratorWrapper.next() } returns entryWrapper
every { entryWrapper.title } returns searchTerm every { entryWrapper.title } returns searchTerm
@ -119,7 +120,8 @@ internal class SearchStateTest {
every { entryWrapper.path } returns "path" every { entryWrapper.path } returns "path"
every { suggestionSearchWrapper.getResults(any(), any()) } returns searchIteratorWrapper every { suggestionSearchWrapper.getResults(any(), any()) } returns searchIteratorWrapper
val searchResultsWithTerm = SearchResultsWithTerm(searchTerm, suggestionSearchWrapper, mockk()) val searchResultsWithTerm =
SearchResultsWithTerm(searchTerm, suggestionSearchWrapper, mockk())
val searchState = SearchState(searchTerm, searchResultsWithTerm, emptyList(), FromWebView) val searchState = SearchState(searchTerm, searchResultsWithTerm, emptyList(), FromWebView)
var list: List<SearchListItem.RecentSearchListItem>? = emptyList() var list: List<SearchListItem.RecentSearchListItem>? = emptyList()
var list1: List<SearchListItem.RecentSearchListItem>? = emptyList() var list1: List<SearchListItem.RecentSearchListItem>? = emptyList()

View File

@ -50,7 +50,6 @@ import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao
import org.kiwix.kiwixmobile.core.reader.ZimFileReader import org.kiwix.kiwixmobile.core.reader.ZimFileReader
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.RecentSearchListItem import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.RecentSearchListItem
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem.ZimSearchResultListItem
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ActivityResultReceived import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ActivityResultReceived
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ClickedSearchInText import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ClickedSearchInText
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ConfirmedDelete import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ConfirmedDelete
@ -123,7 +122,6 @@ internal class SearchViewModelTest {
@Test @Test
fun `SearchState combines sources from inputs`() = fun `SearchState combines sources from inputs`() =
runTest { runTest {
val item = ZimSearchResultListItem("", "")
val searchTerm = "searchTerm" val searchTerm = "searchTerm"
val searchOrigin = FromWebView val searchOrigin = FromWebView
val suggestionSearch: SuggestionSearch = mockk() val suggestionSearch: SuggestionSearch = mockk()

View File

@ -21,7 +21,9 @@ package org.kiwix.kiwixmobile.core.search.viewmodel
import org.kiwix.libzim.SuggestionIterator import org.kiwix.libzim.SuggestionIterator
class SuggestionIteratorWrapper : SuggestionIterator() { class SuggestionIteratorWrapper : SuggestionIterator() {
override fun remove() {} override fun remove() {
// Do nothing just to ignore the EmptyFunctionBlock detekt error.
}
override fun hasNext(): Boolean = super.hasNext() override fun hasNext(): Boolean = super.hasNext()
override fun next(): SuggestionItemWrapper = super.next() as SuggestionItemWrapper override fun next(): SuggestionItemWrapper = super.next() as SuggestionItemWrapper

View File

@ -60,8 +60,8 @@ android {
} }
} }
fun ProductFlavor.createDownloadTask(file: File): Task { fun ProductFlavor.createDownloadTask(file: File): TaskProvider<Task> {
return tasks.create( return tasks.register(
"download${ "download${
name.replaceFirstChar { name.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it"
@ -157,8 +157,8 @@ fun writeZimFileDataInChunk(
outputStream?.close() outputStream?.close()
} }
fun ProductFlavor.createDownloadTaskForPlayAssetDelivery(file: File): Task { fun ProductFlavor.createDownloadTaskForPlayAssetDelivery(file: File): TaskProvider<Task> {
return tasks.create( return tasks.register(
"download${ "download${
name.replaceFirstChar { name.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it"
@ -206,10 +206,10 @@ val String.removeAuthenticationFromUrl: String
fun ProductFlavor.createPublishApkWithExpansionTask( fun ProductFlavor.createPublishApkWithExpansionTask(
file: File, file: File,
applicationVariants: DomainObjectSet<ApplicationVariant> applicationVariants: DomainObjectSet<ApplicationVariant>
): Task { ): TaskProvider<Task> {
val capitalizedName = val capitalizedName =
name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" } name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" }
return tasks.create("publish${capitalizedName}ReleaseApkWithExpansionFile") { return tasks.register("publish${capitalizedName}ReleaseApkWithExpansionFile") {
group = "publishing" group = "publishing"
description = "Uploads $capitalizedName to the Play Console with an Expansion file" description = "Uploads $capitalizedName to the Play Console with an Expansion file"
doLast { doLast {
@ -237,10 +237,10 @@ fun DomainObjectSet<ApplicationVariant>.releaseVariantsFor(productFlavor: Produc
.filter { !it.outputFileName.contains("universal") } .filter { !it.outputFileName.contains("universal") }
.sortedBy { it.versionCodeOverride } .sortedBy { it.versionCodeOverride }
fun ProductFlavor.createPublishBundleWithAssetPlayDelivery(): Task { fun ProductFlavor.createPublishBundleWithAssetPlayDelivery(): TaskProvider<Task> {
val capitalizedName = val capitalizedName =
name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" } name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else "$it" }
return tasks.create("publish${capitalizedName}ReleaseBundleWithPlayAssetDelivery") { return tasks.register("publish${capitalizedName}ReleaseBundleWithPlayAssetDelivery") {
group = "publishing" group = "publishing"
description = "Uploads $capitalizedName to the Play Console with an Play Asset delivery mode" description = "Uploads $capitalizedName to the Play Console with an Play Asset delivery mode"
doLast { doLast {
@ -250,7 +250,7 @@ fun ProductFlavor.createPublishBundleWithAssetPlayDelivery(): Task {
.transactionWithCommit(packageName) { .transactionWithCommit(packageName) {
val generatedBundleFile = val generatedBundleFile =
File( File(
"$buildDir/outputs/bundle/${capitalizedName.lowercase(Locale.getDefault())}" + "${layout.buildDirectory}/outputs/bundle/${capitalizedName.lowercase(Locale.getDefault())}" +
"Release/custom-${capitalizedName.lowercase(Locale.getDefault())}-release.aab" "Release/custom-${capitalizedName.lowercase(Locale.getDefault())}-release.aab"
) )
if (generatedBundleFile.exists()) { if (generatedBundleFile.exists()) {

View File

@ -1,6 +1,6 @@
#Mon Dec 19 16:13:45 IST 2022 #Mon Dec 19 16:13:45 IST 2022
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@ -52,4 +52,5 @@
<ignore path="**/androidTest/**.kt" /> <ignore path="**/androidTest/**.kt" />
</issue> </issue>
<issue id="PrivateResource" severity="warning" /> <issue id="PrivateResource" severity="warning" />
<issue id="MemberExtensionConflict" severity="warning" />
</lint> </lint>