Kotlin .run scoped function - method 'void <init>()' not found - kotlin

I have a problem with the following code snippet:
(Some functions' bodies are ommited for clear view)
fun collectLinks(page: Page): List<String> {
return LinksCrawler().run {
page.accept(this)
this.links
}
}
class LinksCrawler {
private var _links = mutableListOf<String>()
val links
get() = _links.toList()
fun visit(page: Page) { (...) }
fun visit(container: Container) = { (...) }
private fun visit(elements: List<HtmlElement>){ (...) }
}
When I invoke collectLinks() I get
Visitor$LinksCrawler: method 'void ()' not found
(where Visitor is my filename)
As far as I believe, problem would be caused by scope function .run(), maybe that it has no initialisation code that would do sth with LinksCrawler, but correct me if I am wrong.
I do it in .kts file, if it has any meaning. In overall, it is supposed to be an example for a Visitor design pattern. Full file code below:
import Visitor.HtmlElement.Image as Image
import Visitor.HtmlElement.Link as Link
import Visitor.HtmlElement.Table as Table
import Visitor.HtmlElement.Container as Container
main()
// ---------------
fun main() {
val page = Page(Container(Image(), Link(), Image()),
Table(),
Link(),
Container(Table(), Link()),
Container(Image(), Container(Image(), Link())))
println(collectLinks(page))
}
fun collectLinks(page: Page): List<String> {
return LinksCrawler().run {
page.accept(this)
this.links
}
}
class LinksCrawler {
private var _links = mutableListOf<String>()
val links
get() = _links.toList()
fun visit(page: Page) {
visit(page.elements)
}
fun visit(container: Container) = visit(container.elements)
private fun visit(elements: List<HtmlElement>){
for (e in elements) {
when (e) {
is Container -> e.accept(this)
is Link -> _links.add(e.href)
is Image -> _links.add(e.src)
else -> {}
}
}
}
}
fun Container.accept(feature: LinksCrawler) {
feature.visit(this)
}
fun Page.accept(feature: LinksCrawler) = feature.visit(this)
class Page(val elements: MutableList<HtmlElement> = mutableListOf()) {
constructor(vararg elements: HtmlElement) : this(mutableListOf()) {
for (s in elements) {
this.elements.add(s)
}
}
}
sealed class HtmlElement {
class Container(val elements: MutableList<HtmlElement> = mutableListOf()) : HtmlElement() {
constructor(vararg units: HtmlElement) : this(mutableListOf()) {
for (u in units) {
this.elements.add(u)
}
}
}
class Image : HtmlElement() {
val src: String
get() = "http://image"
}
class Link : HtmlElement() {
val href : String
get() = "http://link"
}
class Table : HtmlElement()
}

Related

mocking a class that depends on firebase in a non-uI test for remote config

I have this class that exposes remote configs to others. I thought by creating a class, I would just mock it when testing others that use it but so far, firebase is blocking me. Not sure what I am doing wrong exactly.
class AppRemoteConfig #Inject constructor() {
private var remoteConfig: FirebaseRemoteConfig = Firebase.remoteConfig
private fun setListeningInterval(): Long {
if (BuildConfig.DEBUG){
return 0;
}
return 86400;
}
init {
val configSettings = remoteConfigSettings {
minimumFetchIntervalInSeconds = setListeningInterval()
}
remoteConfig.setConfigSettingsAsync(configSettings)
remoteConfig.setDefaultsAsync(R.xml.remote_config_defaults)
remoteConfig.fetchAndActivate()
.addOnCompleteListener(OnCompleteListener{
if (it.isSuccessful) {
remoteConfig.activate()
}
})
}
fun getString(key: String): String {
return this.remoteConfig.getString(key)
}
}
Now a class uses it this way:
class GetRData #Inject constructor(
private val _remoteConfig: AppRemoteConfig
) {
operator fun invoke(key): String {
try {
return _remoteConfig.getString(key)
} catch(ex: Exception){
return ""
}
return ""
}
}
Now I want to test GetRData class but I get the error: Default FirebaseApp is not initialized in this process null. Make sure to call FirebaseApp.initializeApp(Context) first.
here is what I have tried:
class GetRDataTest {
private var appRemoteConfig = mockk<AppRemoteConfig>(relaxed = true)
private lateinit var getRData : GetRData
#Before
fun setUp(){
getRData = GetRData(appRemoteConfig)
}
#Test
fun `Should get string value`() {
every { appRemoteConfig.getString("status") } returns "red"
val result = getRData.invoke("status")
verify { appRemoteConfig.getString("status") }
Truth.assertThat(result).isEqualTo("red")
}
}

How to mock extensions function in kotlin

I am using Mockk in my project. I am trying to mock my extension function but it cannot find my function. I tried some piece of code but it cannot find the extension function inside my test. Can someone guide me, How can I solve this issue. Thanks
ExploreConsultationsActivity.kt
class ExploreConsultationsActivity : AppCompactActvity () {
... // more function
internal fun setupExploreConsultationVisibility(hasFocus: Boolean) {
if (hasFocus) {
.....
} else if (viewModel.queryText.isEmpty()) {
binding.consultationViewSwitcher.displayConsultationViewSwitcherChild(0)
}
}
internal fun ViewSwitcher.displayConsultationViewSwitcherChild(childNumber: Int) {
visible()
displayedChild = childNumber
}
}
ExploreConsultationsActivityTest.kt
class ExploreConsultationsActivityTest {
#get:Rule
val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule()
private val subject by lazy { spyk(ExploreConsultationsActivity()) }
#MockK private lateinit var mockConsultationViewSwitcher: ViewSwitcher
#Before
fun setUp() {
MockKAnnotations.init(this, relaxed = true)
setupMockView()
}
private fun setupMockView() {
mockkStatic(ExploreConsultationsLayoutBinding::class)
every { mockRootView.findViewById<ChipGroup>(R.id.exploreConsultationChips) } returns mockChipGroup
}
#Test
fun `setupExploreConsultationVisibility - `() {
// STUBBING
mockViewModel.queryText = ""
every { mockViewModel.topicSelected } returns ConsultationTopicsArea.ALL
with(mockConsultationViewSwitcher){
any<ViewSwitcher>().displayConsultationViewSwitcherChild(0)
}
// EXECUTION
subject.setupExploreConsultationVisibility(false)
// VERIFICATION
verify {
mockViewModel.filterBy(ConsultationTopicsArea.ALL)
}
}
I am getting this error

While loop doesn't seem to work with .putFile when uploading multiple images to Firebase storage in Kotlin

I have been trying to upload multiple images to Firebase Storage. But, I am not able to do it successfully. I could successfully upload the image (single) to the storage and add the URL of the image to the Firestore, now that I revised my code to upload up to five images, it could be any number of images from 1 to 5.
R.id.btn_submit -> {
if (validateDetails()) {
uploadImage()
}
}
The above code, calls the following function after validating the fields, which then calls the function uploadImageToCloudStorage. mSelectedImageFileUriList is private var mSelectedImageFileUriList: MutableList<Uri?>? = null. It all seems to work correctly.
private fun uploadImage() {
showProgressDialog(resources.getString(R.string.please_wait))
FirestoreClass().uploadImageToCloudStorage(
this#AddProductActivity,
mSelectedImageFileUriList,
Constants.PRODUCT_IMAGE,
Constants.PRODUCT_IMAGE_DIRECTORY_NAME,
et_product_title.text.toString().trim { it <= ' ' }
)
}
Following code is where I guess is a mistake.
fun uploadImageToCloudStorage(
activity: AddProductActivity,
imageFileURI: MutableList<Uri?>?,
imageType: String,
directoryName: String,
title: String
) {
var i = 0
val imageURLList = ArrayList<String>()
val itr = imageFileURI?.iterator()
if (itr != null) {
while (itr.hasNext()) {
val sRef: StorageReference = FirebaseStorage.getInstance().getReference(
"/$directoryName/" + imageType + "." + Constants.getFileExtension(
activity,
imageFileURI[i]
)
)
sRef.putFile(imageFileURI[i]!!)
.addOnSuccessListener { taskSnapshot ->
taskSnapshot.metadata!!.reference!!.downloadUrl
.addOnSuccessListener { uri ->
if (i < imageFileURI.size) {
i += 1
imageURLList.add(uri.toString())
} else {
activity.imageUploadSuccess(imageURLList)
}
}
}
.addOnFailureListener { exception ->
activity.hideProgressDialog()
Log.e(
activity.javaClass.simpleName,
exception.message,
exception
)
}
}
} else {
Toast.makeText(
activity,
"There is no images in the ArrayList of URI",
Toast.LENGTH_SHORT
).show()
}
}
EDIT: After receiving the first answer.
I have created a QueueSyn.kt file and added the code in the Answer. The activity where the images and the button are changed to
class AddProductActivity : BaseActivity(), View.OnClickListener, QueueSyncCallback {
The following function is called when the button is hit.
private fun uploadProductImage() {
showProgressDialog(resources.getString(R.string.please_wait))
QueueSync(
mSelectedImageFileUriList,
Constants.PRODUCT_IMAGE,
Constants.PRODUCT_IMAGE_DIRECTORY_NAME,
et_product_title.text.toString().trim { it <= ' ' },
this
).startUploading()
}
I have also implemented these two methods in the class AddProductActivity, but I don't know what should go inside this.
override fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>) {
TODO("Not yet implemented")
}
override fun getFileExtension(uri: Uri): String {
TODO("Not yet implemented")
}
Error:
This should work
import android.net.Uri
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import java.util.*
import kotlin.collections.ArrayList
interface QueueSyncCallback {
fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>)
fun getFileExtension(uri: Uri): String
}
class QueueSync(
imageFileURI: MutableList<Uri?>?,
private val imageType: String,
private val directoryName: String,
private val title: String,
private val callback: QueueSyncCallback,
private val maxActive: Int = 5
) {
private val queue: LinkedList<Uri> = LinkedList()
private val runningQueue: MutableList<Uri> = Collections.synchronizedList(
object : ArrayList<Uri>() {
override fun remove(element: Uri): Boolean {
val removed = super.remove(element)
if (isEmpty() && queue.isEmpty()) {
callback.completed(successList, failureList)
} else if (queue.isNotEmpty()) {
addToRunningQueue()
}
return removed
}
}
)
private val successList: MutableList<Uri> = Collections.synchronizedList(ArrayList())
private val failureList: MutableList<Uri> = Collections.synchronizedList(ArrayList())
init {
if (imageFileURI != null)
for (uri in imageFileURI) {
if (uri != null)
queue.add(uri)
}
}
private fun getLocation(uri: Uri) = "/$directoryName/$imageType.${callback.getFileExtension(uri)}"
fun startUploading() {
var i = 0
if (queue.isEmpty()) {
callback.completed(successList, failureList)
return
}
while (i < maxActive && queue.isNotEmpty()) {
addToRunningQueue()
i++
}
}
private fun addToRunningQueue() {
val uri = queue.poll()!!
runningQueue.add(uri)
uploadImageToCloudStorage(uri)
}
private fun uploadImageToCloudStorage(locationUri: Uri) {
val sRef: StorageReference = FirebaseStorage.getInstance().getReference(getLocation(locationUri))
sRef.putFile(locationUri)
.addOnSuccessListener { taskSnapshot ->
taskSnapshot.metadata!!.reference!!.downloadUrl
.addOnSuccessListener { uri ->
successList.add(uri)
runningQueue.remove(locationUri)
}
}
.addOnFailureListener {
failureList.add(locationUri)
runningQueue.remove(locationUri)
}
}
}
Since your need requires usage of threads so to prevent race conditions I had to use Collections.synchronizedList. To use this you need to implement QueueSyncCallback in your activity and pass it as a reference to QueueSync. Make sure that any piece of code written inside completed is wrapped inside runOnMainThread if it is going to access views in any way since completed will not run on main thread as far as I know. This should work however I am not able to test it since it is based on your current code.
Edit:- Answering after edit
override fun completed(successList: MutableList<Uri>, failureList: MutableList<Uri>) {
imageUploadSuccess(successList)
hideProgressDialog()
}
override fun getFileExtension(uri: Uri): String {
Constants.getFileExtension(this, imageFileURI[i])
}

How to put methods in companion object of parent class so that they can be inherited in children classes?

I have two data classes which are very similar to each other.
I want to write a parent class for both of them so they can inherit the common functionality.
My problem is that some methods I want to inherit are needed to be inside companion object.
A) data class Link
import org.json.JSONArray
import org.json.JSONObject
data class Link(
val name: String,
val url: String
) {
var selected: Boolean = false
fun toggle() { selected = selected.not() }
companion object {
fun fromJson(obj: JSONObject): Link = with(obj) {
Link(getString("name"), getString("url"))
}
fun fromJson(arr: JSONArray): List<Link> = with(arr) {
List(length()) {
fromJson(getJSONObject(it))
}
}
fun toJson(list: List<Link>): JSONArray = JSONArray().apply {
list.forEach {
put(it.toJson())
}
}
}
fun toJson(): JSONObject = JSONObject().apply {
put("name", name)
put("url", url)
}
}
B) data class DownloadStatus
import org.json.JSONArray
import org.json.JSONObject
data class DownloadStatus(
val name: String,
val url: String,
val path: String,
var progress: Int = 0
) {
var selected: Boolean = false
fun toggle() { selected = selected.not() }
companion object {
fun fromJson(obj: JSONObject): DownloadStatus = with(obj) {
DownloadStatus(getString("name"), getString("url"), getString("path"), getInt("progress"))
}
fun fromJson(arr: JSONArray): List<DownloadStatus> = with(arr) {
List(length()) {
fromJson(getJSONObject(it))
}
}
fun toJson(list: List<DownloadStatus>): JSONArray = JSONArray().apply {
list.forEach {
put(it.toJson())
}
}
}
fun toJson(): JSONObject = JSONObject().apply {
put("name", name)
put("url", url)
put("path", path)
put("progress", progress)
}
}
abstract class Parent
abstract class Parent {
var selected: Boolean = false
fun toggle() { selected = selected.not() }
companion object {
}
abstract fun toJson(): JSONObject
}
I am stuck here. How to put the methods in companion object of the parent class?

Why there is red line on myPhone.something1.something() ? -something1-

fun main() {
val myPhone = Myphone()
myPhone.phoneOn()
myPhone.onClick()
myPhone.onTouch()
myPhone.openApp()
myPhone.closeApp()
myPhone.brightMax()
myPhone.Something1.something()
}
interface Application {
var appName: String
var x1: Int
fun something()
fun brightMax() {
println("Brightness level is on Max!")
}
fun openApp() {
println("$appName is opening!")
}
fun phoneOn() {
println("The phone is ON")
}
fun onClick() {
println("App is running")
}
fun closeApp() {
println("${Myphone.Something1.appName} App is closed!")
}
}
interface Button {
val x: Int
var helloMessage: String
fun brightMax() {
println("Brightness is on $x")
}
fun phoneOn() {
println("Power on button was pressed!")
}
fun onClick()
fun onTouch() {
println("The screen was touched!")
}
}
class Myphone: Button, Application {
override fun something() {
println("Doing something")
}
object Something1 : Application {
override var x1: Int = 100
override var appName: String = "Instagram"
override fun something() {
println("He wants to die!")
}
}
override var x1: Int = 12
override var appName: String = "Facebook"
override var x: Int = 100
override fun phoneOn() {
super<Application>.phoneOn()
}
override fun brightMax() {
super<Application>.brightMax()
super<Button>.brightMax()
}
override var helloMessage: String = "Hello"
override fun onClick() {
super.onClick()
}
}
I created object inside the class and I can't "call" it back in main function.
Once I did and I can't remember how to solve it again.
Don't blame me because of code. I made it for presentation.
on the 9th line, there is error, why? the something1 has red line in kotlin.
something1.appName - is working perfectly?
You get the error because it's not recommended to access nested objects via instance references. Use Myphone.Something1.something() instead of myPhone.Something1.something().