Fixes 4544 : Language selection: history (#4880)

* Xml changes

* Content provider created

* Database setup done

* Database setup revised

* Database setup revised

* SettingsFragment finished

* SettingsFragment finished

* UploadMediaDetailFragment updated

* UploadMediaDetailFragment updated

* Java docs

* Test fixed

* Test added

* Test added

* Test updated

* More tests added
This commit is contained in:
Ayan Sarkar 2022-03-23 13:03:54 +05:30 committed by GitHub
parent 85bdcd5a7a
commit 4194409cd2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1312 additions and 9 deletions

View file

@ -0,0 +1,114 @@
package fr.free.nrw.commons.recentlanguages
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.TestCommonsApplication
import kotlinx.android.synthetic.main.row_item_languages_spinner.view.*
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
import java.lang.reflect.Field
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
@LooperMode(LooperMode.Mode.PAUSED)
class RecentLanguagesAdapterUnitTest {
private lateinit var adapter: RecentLanguagesAdapter
private lateinit var languages: List<Language>
@Mock
private lateinit var context: Context
@Mock
private lateinit var viewGroup: ViewGroup
@Mock
private lateinit var convertView: View
@Mock
private lateinit var textView: TextView
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
languages = listOf(
Language("English", "en"),
Language("Bengali", "bn")
)
adapter = RecentLanguagesAdapter(context, languages, hashMapOf(1 to "en"))
}
@Test
@Throws(Exception::class)
fun checkAdapterNotNull() {
Assert.assertNotNull(adapter)
}
@Test
@Throws(Exception::class)
fun testIsEnabled() {
val list = languages
val recentLanguagesAdapter: Field =
RecentLanguagesAdapter::class.java.getDeclaredField("recentLanguages")
recentLanguagesAdapter.isAccessible = true
recentLanguagesAdapter.set(adapter, list)
Assert.assertEquals(adapter.isEnabled(0), false)
}
@Test
@Throws(Exception::class)
fun testGetCount() {
val list = languages
val recentLanguagesAdapter: Field =
RecentLanguagesAdapter::class.java.getDeclaredField("recentLanguages")
recentLanguagesAdapter.isAccessible = true
recentLanguagesAdapter.set(adapter, list)
Assert.assertEquals(adapter.count, list.size)
}
@Test
@Throws(Exception::class)
fun testGetLanguageName() {
val list = languages
val recentLanguagesAdapter: Field =
RecentLanguagesAdapter::class.java.getDeclaredField("recentLanguages")
recentLanguagesAdapter.isAccessible = true
recentLanguagesAdapter.set(adapter, list)
val languageName = list[0].languageName
Assert.assertEquals(adapter.getLanguageName(0), languageName)
}
@Test
@Throws(Exception::class)
fun testGetView() {
val list = languages
whenever(convertView.tv_language).thenReturn(textView)
val recentLanguagesAdapter: Field =
RecentLanguagesAdapter::class.java.getDeclaredField("recentLanguages")
recentLanguagesAdapter.isAccessible = true
recentLanguagesAdapter.set(adapter, list)
Assert.assertEquals(adapter.getView(0, convertView, viewGroup), convertView)
}
@Test
@Throws(Exception::class)
fun testGetLanguageCode() {
val list = languages
val recentLanguagesAdapter: Field =
RecentLanguagesAdapter::class.java.getDeclaredField("recentLanguages")
recentLanguagesAdapter.isAccessible = true
recentLanguagesAdapter.set(adapter, list)
val languageCode = list[0].languageCode
Assert.assertEquals(adapter.getLanguageCode(0), languageCode)
}
}

View file

@ -0,0 +1,37 @@
package fr.free.nrw.commons.recentlanguages
import android.net.Uri
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.data.DBOpenHelper
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.powermock.reflect.Whitebox
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
class RecentLanguagesContentProviderUnitTest {
private lateinit var contentProvider: RecentLanguagesContentProvider
@Mock
lateinit var dbOpenHelper: DBOpenHelper
@Before
fun setUp(){
MockitoAnnotations.initMocks(this)
contentProvider = RecentLanguagesContentProvider()
Whitebox.setInternalState(contentProvider, "dbOpenHelper", dbOpenHelper)
}
@Test
@Throws(Exception::class)
fun testGetType() {
contentProvider.getType(mock(Uri::class.java))
}
}

View file

@ -0,0 +1,227 @@
package fr.free.nrw.commons.recentlanguages
import android.content.ContentProviderClient
import android.content.ContentValues
import android.database.Cursor
import android.database.MatrixCursor
import android.database.sqlite.SQLiteDatabase
import android.os.RemoteException
import com.nhaarman.mockitokotlin2.*
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.*
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
class RecentLanguagesDaoUnitTest {
private val columns = arrayOf(
COLUMN_NAME,
COLUMN_CODE
)
private val client: ContentProviderClient = mock()
private val database: SQLiteDatabase = mock()
private val captor = argumentCaptor<ContentValues>()
private lateinit var testObject: RecentLanguagesDao
private lateinit var exampleLanguage: Language
/**
* Set up Test Language and RecentLanguagesDao
*/
@Before
fun setUp() {
exampleLanguage = Language("English", "en")
testObject = RecentLanguagesDao { client }
}
@Test
fun createTable() {
onCreate(database)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
}
@Test
fun deleteTable() {
onDelete(database)
inOrder(database) {
verify(database).execSQL(DROP_TABLE_STATEMENT)
}
}
@Test
fun createFromCursor() {
createCursor(1).let { cursor ->
cursor.moveToFirst()
testObject.fromCursor(cursor).let {
Assert.assertEquals("languageName", it.languageName)
Assert.assertEquals("languageCode", it.languageCode)
}
}
}
@Test
fun testGetRecentLanguages() {
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
.thenReturn(createCursor(14))
val result = testObject.recentLanguages
Assert.assertEquals(14, (result.size))
}
@Test(expected = RuntimeException::class)
fun getGetRecentLanguagesTranslatesExceptions() {
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(
RemoteException("")
)
testObject.recentLanguages
}
@Test
fun getGetRecentLanguagesReturnsEmptyList_emptyCursor() {
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull()))
.thenReturn(createCursor(0))
Assert.assertTrue(testObject.recentLanguages.isEmpty())
}
@Test
fun getGetRecentLanguagesReturnsEmptyList_nullCursor() {
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(null)
Assert.assertTrue(testObject.recentLanguages.isEmpty())
}
@Test
fun cursorsAreClosedAfterGetRecentLanguages() {
val mockCursor: Cursor = mock()
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenReturn(mockCursor)
whenever(mockCursor.moveToFirst()).thenReturn(false)
testObject.recentLanguages
verify(mockCursor).close()
}
@Test
fun findExistingLanguage() {
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(1))
Assert.assertTrue(testObject.findRecentLanguage(exampleLanguage.languageCode))
}
@Test(expected = RuntimeException::class)
fun findLanguageTranslatesExceptions() {
whenever(client.query(any(), any(), anyOrNull(), any(), anyOrNull())).thenThrow(
RemoteException("")
)
testObject.findRecentLanguage(exampleLanguage.languageCode)
}
@Test
fun findNotExistingLanguageReturnsNull_emptyCursor() {
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(createCursor(0))
Assert.assertFalse(testObject.findRecentLanguage(exampleLanguage.languageCode))
}
@Test
fun findNotExistingLanguageReturnsNull_nullCursor() {
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(null)
Assert.assertFalse(testObject.findRecentLanguage(exampleLanguage.languageCode))
}
@Test
fun cursorsAreClosedAfterFindLanguageQuery() {
val mockCursor: Cursor = mock()
whenever(client.query(any(), any(), any(), any(), anyOrNull())).thenReturn(mockCursor)
whenever(mockCursor.moveToFirst()).thenReturn(false)
testObject.findRecentLanguage(exampleLanguage.languageCode)
verify(mockCursor).close()
}
@Test
fun migrateTableVersionFrom_v1_to_v2() {
onUpdate(database, 1, 2)
// Table didnt exist before v7
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v2_to_v3() {
onUpdate(database, 2, 3)
// Table didnt exist before v7
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v3_to_v4() {
onUpdate(database, 3, 4)
// Table didnt exist before v7
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v4_to_v5() {
onUpdate(database, 4, 5)
// Table didnt exist before v7
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v5_to_v6() {
onUpdate(database, 5, 6)
// Table didnt exist before v7
verifyZeroInteractions(database)
}
@Test
fun migrateTableVersionFrom_v6_to_v7() {
onUpdate(database, 6, 7)
verify(database).execSQL(CREATE_TABLE_STATEMENT)
}
@Test
fun migrateTableVersionFrom_v7_to_v8() {
onUpdate(database, 7, 8)
// Table didnt change in version 8
verifyZeroInteractions(database)
}
@Test
fun testAddNewLanguage() {
testObject.addRecentLanguage(exampleLanguage)
verify(client).insert(eq(RecentLanguagesContentProvider.BASE_URI), captor.capture())
captor.firstValue.let { cv ->
Assert.assertEquals(2, cv.size())
Assert.assertEquals(
exampleLanguage.languageName,
cv.getAsString(COLUMN_NAME)
)
Assert.assertEquals(
exampleLanguage.languageCode,
cv.getAsString(COLUMN_CODE)
)
}
}
@Test
fun testDeleteLanguage() {
testObject.addRecentLanguage(exampleLanguage)
testObject.deleteRecentLanguage(exampleLanguage.languageCode)
}
private fun createCursor(rowCount: Int) = MatrixCursor(columns, rowCount).apply {
for (i in 0 until rowCount) {
addRow(listOf("languageName", "languageCode"))
}
}
}

View file

@ -1,16 +1,32 @@
package fr.free.nrw.commons.settings
import android.app.Dialog
import android.content.Context
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.widget.AdapterView
import android.widget.ListView
import android.widget.TextView
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.recentlanguages.Language
import fr.free.nrw.commons.recentlanguages.RecentLanguagesAdapter
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao
import fr.free.nrw.commons.upload.UploadMediaDetailAdapter
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.powermock.reflect.Whitebox
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
@ -29,6 +45,21 @@ class SettingsFragmentUnitTests {
private lateinit var layoutInflater: LayoutInflater
private lateinit var context: Context
@Mock
private lateinit var recentLanguagesDao: RecentLanguagesDao
@Mock
private lateinit var recentLanguagesTextView: TextView
@Mock
private lateinit var separator: View
@Mock
private lateinit var languageHistoryListView: ListView
@Mock
private lateinit var adapterView: AdapterView<RecentLanguagesAdapter>
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@ -42,6 +73,13 @@ class SettingsFragmentUnitTests {
fragmentTransaction.commitNowAllowingStateLoss()
layoutInflater = LayoutInflater.from(activity)
Whitebox.setInternalState(fragment, "recentLanguagesDao", recentLanguagesDao)
Whitebox.setInternalState(fragment, "recentLanguagesTextView",
recentLanguagesTextView)
Whitebox.setInternalState(fragment, "separator", separator)
Whitebox.setInternalState(fragment, "languageHistoryListView",
languageHistoryListView)
}
@Test
@ -107,6 +145,41 @@ class SettingsFragmentUnitTests {
method.invoke(fragment, "", "appUiDefaultLanguagePref")
}
@Test
@Throws(Exception::class)
fun `Test prepareAppLanguages when recently used languages is empty`() {
Shadows.shadowOf(Looper.getMainLooper()).idle()
val method: Method = SettingsFragment::class.java.getDeclaredMethod(
"prepareAppLanguages",
String::class.java
)
method.isAccessible = true
method.invoke(fragment, "appUiDefaultLanguagePref")
verify(recentLanguagesDao, times(1)).recentLanguages
}
@Test
@Throws(Exception::class)
fun `Test prepareAppLanguages when recently used languages is not empty`() {
Shadows.shadowOf(Looper.getMainLooper()).idle()
whenever(recentLanguagesDao.recentLanguages)
.thenReturn(
mutableListOf(Language("English", "en"),
Language("English", "en"),
Language("English", "en"),
Language("English", "en"),
Language("English", "en"),
Language("English", "en"))
)
val method: Method = SettingsFragment::class.java.getDeclaredMethod(
"prepareAppLanguages",
String::class.java
)
method.isAccessible = true
method.invoke(fragment, "appUiDefaultLanguagePref")
verify(recentLanguagesDao, times(2)).recentLanguages
}
@Test
@Throws(Exception::class)
fun testSaveLanguageValueCase_descriptionDefaultLanguagePref() {
@ -120,4 +193,77 @@ class SettingsFragmentUnitTests {
method.invoke(fragment, "", "descriptionDefaultLanguagePref")
}
@Test
@Throws(Exception::class)
fun testHideRecentLanguagesSection() {
Shadows.shadowOf(Looper.getMainLooper()).idle()
val method: Method = SettingsFragment::class.java.getDeclaredMethod(
"hideRecentLanguagesSection"
)
method.isAccessible = true
method.invoke(fragment)
verify(recentLanguagesTextView, times(1)).visibility = any()
verify(separator, times(1)).visibility = any()
verify(languageHistoryListView, times(1)).visibility = any()
}
@Test
@Throws(Exception::class)
fun testOnRecentLanguageClicked() {
whenever(recentLanguagesDao.findRecentLanguage(any()))
.thenReturn(true)
whenever(adapterView.adapter)
.thenReturn(RecentLanguagesAdapter(context,
listOf(Language("English", "en")),
hashMapOf<String,String>()
)
)
val method: Method = SettingsFragment::class.java.getDeclaredMethod(
"onRecentLanguageClicked",
String::class.java,
Dialog::class.java,
AdapterView::class.java,
Int::class.java
)
method.isAccessible = true
method.invoke(fragment, "test", Mockito.mock(Dialog::class.java), adapterView, 0)
verify(recentLanguagesDao, times(1)).findRecentLanguage(any())
verify(adapterView, times(2)).adapter
}
@Test
fun `Test setUpRecentLanguagesSection when list is empty`() {
val method: Method = SettingsFragment::class.java.getDeclaredMethod(
"setUpRecentLanguagesSection",
List::class.java,
HashMap::class.java
)
method.isAccessible = true
method.invoke(fragment, emptyList<Language>(), hashMapOf<Int,String>())
verify(languageHistoryListView, times(1)).visibility = View.GONE
verify(separator, times(1)).visibility = View.GONE
verify(recentLanguagesTextView, times(1)).visibility = View.GONE
}
@Test
fun `Test setUpRecentLanguagesSection when list is not empty`() {
val method: Method = SettingsFragment::class.java.getDeclaredMethod(
"setUpRecentLanguagesSection",
List::class.java,
HashMap::class.java
)
method.isAccessible = true
method.invoke(fragment, listOf(
Language("Bengali", "bn"),
Language("Bengali", "bn"),
Language("Bengali", "bn"),
Language("Bengali", "bn"),
Language("Bengali", "bn"),
Language("Bengali", "bn")
), hashMapOf<Int,String>())
verify(languageHistoryListView, times(1)).visibility = View.VISIBLE
verify(separator, times(1)).visibility = View.VISIBLE
verify(recentLanguagesTextView, times(1)).visibility = View.VISIBLE
}
}

View file

@ -1,15 +1,27 @@
package fr.free.nrw.commons.upload
import android.app.Dialog
import android.content.Context
import android.view.View
import android.widget.AdapterView
import android.widget.GridLayout
import android.widget.ListView
import android.widget.TextView
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.recentlanguages.Language
import fr.free.nrw.commons.recentlanguages.RecentLanguagesAdapter
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao
import fr.free.nrw.commons.settings.SettingsFragment
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.powermock.reflect.Whitebox
import org.robolectric.Robolectric
@ -18,6 +30,7 @@ import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
import java.lang.reflect.Field
import java.lang.reflect.Method
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
@ -36,15 +49,33 @@ class UploadMediaDetailAdapterUnitTest {
@Mock
private lateinit var eventListener: UploadMediaDetailAdapter.EventListener
@Mock
private lateinit var recentLanguagesDao: RecentLanguagesDao
@Mock
private lateinit var textView: TextView
@Mock
private lateinit var view: View
@Mock
private lateinit var listView: ListView
@Mock
private lateinit var adapterView: AdapterView<RecentLanguagesAdapter>
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
uploadMediaDetails = mutableListOf(uploadMediaDetail, uploadMediaDetail)
activity = Robolectric.buildActivity(UploadActivity::class.java).get()
adapter = UploadMediaDetailAdapter("")
adapter = UploadMediaDetailAdapter("", recentLanguagesDao)
context = RuntimeEnvironment.application.applicationContext
Whitebox.setInternalState(adapter, "uploadMediaDetails", uploadMediaDetails)
Whitebox.setInternalState(adapter, "eventListener", eventListener)
Whitebox.setInternalState(adapter, "recentLanguagesTextView", textView)
Whitebox.setInternalState(adapter, "separator", view)
Whitebox.setInternalState(adapter, "languageHistoryListView", listView)
viewHolder = adapter.onCreateViewHolder(GridLayout(activity), 0)
}
@ -145,4 +176,75 @@ class UploadMediaDetailAdapterUnitTest {
verify(uploadMediaDetail).isManuallyAdded
}
@Test
fun testHideRecentLanguagesSection() {
val method: Method = UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod(
"hideRecentLanguagesSection"
)
method.isAccessible = true
method.invoke(viewHolder)
verify(listView, times(1)).visibility = View.GONE
verify(view, times(1)).visibility = View.GONE
verify(textView, times(1)).visibility = View.GONE
}
@Test
fun `Test setUpRecentLanguagesSection when list is empty`() {
val method: Method = UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod(
"setUpRecentLanguagesSection",
List::class.java
)
method.isAccessible = true
method.invoke(viewHolder, emptyList<Language>())
verify(listView, times(1)).visibility = View.GONE
verify(view, times(1)).visibility = View.GONE
verify(textView, times(1)).visibility = View.GONE
}
@Test
fun `Test setUpRecentLanguagesSection when list is not empty`() {
val method: Method = UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod(
"setUpRecentLanguagesSection",
List::class.java
)
method.isAccessible = true
method.invoke(viewHolder, listOf(
Language("Bengali", "bn"),
Language("Bengali", "bn"),
Language("Bengali", "bn"),
Language("Bengali", "bn"),
Language("Bengali", "bn"),
Language("Bengali", "bn")
))
verify(listView, times(1)).visibility = View.VISIBLE
verify(view, times(1)).visibility = View.VISIBLE
verify(textView, times(1)).visibility = View.VISIBLE
}
@Test
@Throws(Exception::class)
fun testOnRecentLanguageClicked() {
whenever(recentLanguagesDao.findRecentLanguage(any()))
.thenReturn(true)
whenever(adapterView.adapter)
.thenReturn(
RecentLanguagesAdapter(context,
listOf(Language("English", "en")),
hashMapOf<String,String>()
)
)
val method: Method = UploadMediaDetailAdapter.ViewHolder::class.java.getDeclaredMethod(
"onRecentLanguageClicked",
Dialog::class.java,
AdapterView::class.java,
Int::class.java,
UploadMediaDetail::class.java
)
method.isAccessible = true
method.invoke(viewHolder, Mockito.mock(Dialog::class.java), adapterView, 0,
Mockito.mock(UploadMediaDetail::class.java))
verify(recentLanguagesDao, times(1)).findRecentLanguage(any())
verify(adapterView, times(3)).adapter
}
}